My C# Notes
The .Net frame work is component based. All C# objects can be thought of as components.
Each object is associcated with metadata, i.e. data about that object.
Each program requires a main function.
public static void Main(string[] args)
{
}
The main function is static. Main is the function that controls everything, static prevents you creating more instances of the class containing the main function.
Code is seperated into classes. Its an OOP paradaigm so each class should attempt to represent some distinct object. Classes that interact with each other, are grouped together in the same scope space. These spaces are defined using the namespace keyword. Namespaces are used to group and order classes, they can be nested in each other much like Russian Dolls.
namespace DanSpace
{
//classes concerning the operation of DanSpace
}
Once these namespaces are carved out, other parts of the program can gain some level of access to them by using the using keyword. You may want to use a deeply nested namespace this can be done by using an alias.
using theConsoleClass = System.Console;
...
theConsoleClass.Writeln("I'm using an alias!");
.Net allows you to write in any programming language it supports. You can the package up whatever you've written and access its functionality in any other programming class. Package up some code is turning it into an assembly. To access other users objects in assemblies you must locate them and add them as references in the IDE.
Structs exist in C# also. They are mainly used for lightweight datatypes. Made to be used as the built in datatypes are used. So adding matrices or vectors or complex numbers etc.
Attributes are a part of the metadata mentioned earlier. Attributes are stored in square brackets.
e.g
[Code Review("1.02.2003", Comment = "Well Done"]
The specific of the attribute are defined usng a special class we'll come to later.
Attributes can be accessed during runtime!
C# supports exception heavily. Rather than have some function returning a HRESULT choc-a-block with various error codes, errors are thrown as exceptions. This makes the code much less buggy, as error handling is seperated and all the expensive checking and such is used only when needed. If an error occurs the program throws it down the code, until it reaches the relevant ctach statement. If it doesn't find the catch statement, it goes up a level in scope and tries again. If it once failes all the way to the top level of scope a little window will come up and the program will be halted. Such as DivdeByZeroException press okay.
They work like this
try
{
int j = 9 / ZERO
}
catch (DivideByZeroException e)
{
//Do correction code.
}
A catcher might do some clean up code and then throw the exeception again. It may throw a new expection so another program can do some more cleaning up.
Users can defined their own expections. Using inheritance and making sure to use the right overloaders.
public class CountIsZeroException : Exception
{
public CountIsZeroException()
{
}
public CountIsZeroException(string message) : base (message)
{
}
}
So there's the new exception and we might use it as below.
public void DoAverage()
{
if (count == 0)
throw( new CountIsZeroException("Zero Exception in DoAverage")
else
average = sum / count;
}
The Finally keyword, with all these exception hoping out at unknown times, it may leave resouces open, and not properly shut down. Such as I/O for example. This is where the finally keyword comes into play. No matter what before a function is exited the finally cdoe block is called.
finally
{
file.Close();
}
Classes
myPoint.GetMyPoint(ref x, ref y);
X and Y must both be intialized
myPoint.GetMyPoint(out x, out y);
X and Y do not have to be intialized
Overwriting
If a base class has a function 'foo' and the inhertiting class has a function 'foo' which is an unrelated class. Then the child class must make this clear to the complier by adding in the keyword 'new'.
public new update ( params )
{
}
What if we wish the function to replace the base classes function? The base function must be declared to be virtual and the child must use the override keyword.
virtual public update(); // in base class
override public update(); // in child class
Now if the child class was referenced as the base class and the update function was called, then the complier would check the type of the object. It would then check if the function was overridden and if so would call the overriding class.
So new is for a seperate unrelated class that happen to share the same name
But virtual and override mean that the functions is to be overwritten by its child function.
Programming Style: The Virtual Keyword should only be added when used. (only added when there is a base class overriding)
Abstract Class
An abstract class is a class with one or more abstract functions.
An abstract function is a function that has no functionality. Its just saying there should be a function that takes this and this and returns this.
An abstract class therefore can be instaiated. You cannot create a class with function that have no code.
Any child of an anstract class that is not abstract must include code for the abstract functions. Using the override keyword.
This is a powerful enforcing tool.
The Sealed keyword means a class cannot be inherited from.
sealed class MyClass
{
}
Class privacy
Internal - used to write helper classes, that you would rather the final user of the class did not see.
access to the class is avaliable to all classes in the same assembly.
it can also be used on members of a class, when you wish to share members but not allow public access.
Internal may be combined to internal protected. So that those classes accessing through protected or internal have access.
Classes as we said earlier can be nested. This is useful if only the current class is going to make use of that type of object. Not only classes can be nested in classes so can structs, enumerations and interfaces.
Constructors
A function that is called as soon as an object is created from the class.
The function must be public, share the same name as the class and not have any return types.
It is often useful to call the base classes constructor first and this is done as following:
public class Derived : BaseClass
{
public Derived (int x) : Base(x)
{
}
}
Constructors often repeat a lot of code, for the various ways they can be constructed. Usually building up from simplier intializations in this way forwarding can be useful.
public MyObject(int x)
{
this.x = x
}
public MyObject(int x, int y): this(x)
{
this.y = y
}
The second constructor calls the first. Less code has to be written.
Elements in a class can automatically be intialzed to some value.
This is done simply
int x = 0;
Static members and functions are those that are not asssociated with any particular instance but rather the class itself in general.
One of there uses is as class counters.
class MyClass
{
public MyClass()
{
instanceCount++;
}
public static int instanceCount = 0;
}
Outside of the class, in the main code. To access the static member the class name must be used not the name of one of its object instances. So it would be MyClass.instanceCount, rather than someObject.instanceCount. That would not work.
Static constructor can also exist, before the first objecti s intialized the function is called and my do necessary setup work.
Constants use the keyword const, and must be set at compile time, they will not change.
If we wish to have something like a constant but do not know its value until the program is running the readonly keyword can be used. Once assigned a value this value cannot be changed.
If you choose to create a class of all static members it is best to declare it private so that it cannot accidently be initailized.
Creating A Variable Length Parameter List
Such as writeln(string1, string2, .... stringn). They can all be written out. This can be achieved in user-defined classes also. The keyword params is used.
//first deal with the single argument condition
public void Write(string label, object arg)
{
}
// the deal with the n-case, an array of arguments
public void Write (string label, params object[] args)
params keyword means that the function will accept variable length arguments.
It is good programming practice to include a few function for some of the simplier cases also. I.e. one argument, two arguments, three arguments and the array of arguments.
Structs
Structs are cheap fast to allocate and do not have a reference overhead.
They are the same as classes but
- cannot inherit from any other type
- other classes may not inherit from them
They should only be used for the basic types.
Interface
Interface classes are those classes that have all there methods abstract. A class may inherit from more that one interface but it may inherit only from one class.
interface IScalable
{
void ScaleX(float factor)
void ScaleY(float factor)
}
foreach (DiagramObject d in dArray)
{
IScalable scalable = d as IScalable;
if( scalable !=null)
{
scalable.ScaleX(10.0F)
scalable.ScaleY(10.0F);
}
}
The as keyword changes all objects to the type after the as, or sets the others to null, if it cannot be explicitly converted.
Choosing between Abstract and Interface: - If objects derived from base class would cary widely then Interface is the more suited choice.
Bits and Bobs
Case statements are more efficent than nested ifs.
foreach example
foreach (MyObject current in arr)
{
Console.Writeln("Item: {0}", current);
}
It can also be used to iterate across the keys of a hash table.
Blocks of code can be put into checked status by:
checked
{
//code
}
Any overflows will then thorw expections. Going out of range by repeatadly adding to a byte etc.
The entire program can be put into check by cheking a falg in the ide.
Then when you want something now to raise the exception you should but it in unchecked blocks.
Remainder (I always forget this one so I'm putting it in) x % y =~ x-(x/y) * y
Conditional operator
? :
int value - (x<10) ? 15 : 0;
If the condition evaluates to true then value is assigned 15 otherwise 0.
TypeOf - reutrns the type of the current object
Is - can an object be converter to a particular type, returns a bool.
Arrays
Operators: Reverse(), IndexOf(), LastIndexOf(), Sort(), BinarySearch()
The sort works for built in types and cane be extended so that it will work for user defined types also.
Accesors
public string Name
{
get
{
return Name;
}
set
{
value = Name;
}
}
If you have a getter only its is readonly.
If you have a setter only its is writeonly.
These are useful for effecient loading.
class CarDealer
{
public int ProductionCount
{
get
{
if(productionCount == -1)
{
// do expensive load operation from the database
}
}
}
int productionCount = -1;
}
Use the negative value of -1 to mean that we have not read in from the database. Thereby only doing a database access when need be.
Enumerators
Useful for a value in a program can only have a certain set of values.
Enumerators can also be used as Flags bits allowing them to be comibined using logical operations.
[Flags]
EnumBitValues
{
{
NoBits = 0;
Bit1 = 0x00000...1
...
}
}
Attributes - annotations that are placed in the source code.
Delegate
A bit like type safe fuction pointrers.
They are created at runtime and specify the form of a single function.
If two delegates are added together a delgate that calls both functions is the result.
Useful for when you want to change the behaviour of a function.
Example: A sorting function where the delegate defines the comparision function.
public delegate int CompareItemsCallback(object obj1, object obj2);
public void Sort(CompareItemsCallBack compare)
{
//inner loop
int order = compare(item1, item2)
}
public class Employee
{
public static int CompareName(Object obj1, Object obj2)
{
Employee emp1 = (Employee) obj1;
Employee emp2 = (Employee) obj2;
return(string.compare(emp1.name, emp2.name))
}
public static int CompareId(Object obj1, Object obj2)
{
//compare ids
}
}
...
//Main Function
//Create Delegate To Sort by Names!
Container.CompareItemsCallback sortByName = new Container.CompareItemsCallback(Employee.CompareName);
employees.Sort(sortbyName)
}
THats how delgates work how fiendishly clever. Things though can infact get cleverer. In the book I was perusing things got incrementally clever-ver. Rather than do that I'm going to jump to the last step of cleverness.
Create the delegates on the fly rather than always having them present.
public static Container.CompareItemsCallBack SortByName
{
get
{
return( new Container.CompareItemsCallBack SortByName
}
}
Now only created when needed.
Events - a class can use events to notify another class/ classes that something has happened.
Publish / Subscribe idiom.
A class publishes events it can raise. Classes that are interested in a specific event can subscribe to it.
Often used for GUIs, but can also be used for things like mail handlers.
User Defined Conversions - allow conversions between classes and structs and other objects in the system. The example for this was pretty awful.
Basically you have to take in account if you may loose data or not. Epxplicit and Implicit conversion. Currently I don't see myself using it so I've skimmed it.
Infix Operators!
We use the operator keyword to allow a class to use the +, - and others. Overload them!
public static Datatype operator + (Datatype D1, Datatype D2)
{
//Do the jazz return the result
}
Preprosssors
#define DEBUGLOG
...
//In code
# if DEBUGLOG
Console.Writeln("Main - Debug is enabled");
#else
Console.Writeln("No Debug in Main");
#endif
Verbatim Strings
Removes some of the string processing so you can write exactly what gets printed out.
string = "C:\\Program Files\\Microsoft Office\\Office"
instead we can have
string = @"C:\program Files\Microsfot Office\Office"
It will also keep all tabs and newline you phyically put in, using vertabim strings may span several lines.
Making Objects the fit in with the dotNet frame work
All objects have the operators '==', '!=' and toString. These can be overridden so that they are more useful.
ToString:
override public string ToString()
{
return ("{0} \n {1} \\ {2}", name, health, hits);
}
...
Console.Writeln("Pete's health:{0}", pete);
//Writes out pete name and what health he has out of his total
Equals() used by collections(hash tables, arrays ...) for ordering . For more useful ordering it should be overwritten with code that says when two of your classes should be considered equal. For example two pieces of wood should only stack if they have the same durability.
public override bool Equals(objects obj)
{
Item item2 = (item) obj;
if durability != item2.durability
return false;
if name != item2.name
return false;
return true;
}
Once equals is defined its easy to write in the '==' and '!=' equals operators.
public static bool operator == (Item I1, Item I2)
{
return(I1.Equals(I2));
}
public static bool operator != (Item I1, Item I2)
{
return(!I1.Equals(I2));
}
These operator '==' and '!=' must be overloaded as a pair. They cannot be overloaded seperately
GetHashCode() must return a unique integer id for the object. This id must be related to the value(s) used to determine if the object is equal or not to another object. If there is a unique field in the object it is probably a good idea to use this as the hash code.
public override int GetHashCode()
{
return(unique_id);
}
If there is no unique Id then one should be created from a function of values.
Making User Defined Types Sortable
Simplest way is to implement the IComparable interface.
This allows you to add sorting, but sorting only one way. What if you wanted to sort by different field, name, age, shoe size, well we'll come to that afterwards.
First the IComparable Interface
int IComparable.CompareTo(object obj)
{
Employee emp2 = (Employee) obj;
if (this.id > emp2.id)
return(1);
if (this.id < emp2.id)
return(-1)
else
return(0);
}
So a int for the three possiblities of greater than(1), less than(-1) and equal(0)
This could be seens as a default sort. Though it does not let the user deicde what he would like a group of classes to sorte by.
To do this the IComparer interface must be used.
The sorting classes are nested in the class they define the sort for.
pubic class Employee : IComparable
{
public Employee() {} // constructor
int IComparable.CompareTo(Object obj) //default sort
public class sortByNameClass : IComparer // User defined sort using nested class
{
public int Compare(Object obj1, Object obj2)
{
//Do comparision using string.Compare
}
}
}
....
// In the main body of code
Array.Sort(arr, (IComparer) new Employee.SortByNameClass());
//Now the array is sorted by name
The syntax is a little messy with the 'new Employee.SortByNameClass());' The user shouldn't have to create a new object, if we make it staic the call be comes a lot simplier as the class doesn't have to be intialized.
public static.IComparerSortByName
{
get
{
return( (IComparer) new SortByNameClass());
}
}
public static.IComparer.SortById
{
get
{
return(IComparer) new SortByIdClass());
}
}
These make the calls to the instaitated the classes, so the user no longer has to instead he just calls the accessors.
As below:
Array.Sort(arr, Employee.SortByName);
For completeness the Relational Operators should also be overloaded.
(<,>, <=, >=)
This is done as before. Note they must be overloaded in pairs you cannot just overload one.
public static bool operator < (Employee emp1, Employee emp2)
{
IComparable icomp = (IComparable) emp1
return(icomp.CompareTo(Emp2) > 0);
}
Advanced Hashing
Very similar to adding new sort classes.
When you wish for an object to have more than one hash. i.e. based on the EmployeesID and another based on the EmployeesName.
IHashCodeProvider Interface should be used.
IClonable
Object.MemberWiseClone();
Creates merely a shallow clone. All references are copied, rather than creating new objects to be referenced to.
What is needed is a deep copy, where new objects are created.
This is done by implementing the IClonable Interface
class ContainedValue
{
public ContainedValue(int Count)
{
this.count = count;
}
public int count;
}
class MyObject : ICloneable
{
public MyObject(int Count)
{
this.contained = new ContainedValue(Count);
}
public Object Clone()
{
Console.Writeln("Clone")
return new MyObject(this.Contained.Count);
}
public ContainedValue Contained;
}
Some brief I/O
class Test
{
public static void Main()
{
FileStream f = new FileStream("output.txt", filemode.create);
StreamWriter s = new StreamWriter(f);
s.WriteLine("{0} {1}", "text", 55);
s.close;
f.close;
}
}
Style
PascalCasing - anything that would be visible externally from the class
camelCasing - private members, method parameters.
Interfaces should start with I.
Assert
using System.Diagnostics;
Debug.Assert(i==0, "BadState");
If i equals 0 then bad state is outputted.
In main:
Debug.Listeners.Clear(); //remove default Error Outputs
Debug.Listeners.Add(new TextWriter.Trace.Listener(Console.Out));
The default output is a windows message box, here its changed to the console window.
WriteIf and WriteLineIf, only write out if there first parameter is true.
Debug.WriteLineIf(debugOutput, "In VerifyState");
No comments:
Post a Comment