C# Language
Last Updated: 2001
Reserved words can be used for variable names if prefixed with @ symbol. If
you prefix a non-reserved word with @ it is no different to the unreserved work
(e.g. @fred==fred)
Variables names are UNICODE based, so can include foreign characters. Usual
first letter, number or _ rule applies.
JIT Compilation
- Standard JIT
Produces and caches highly optimised machine code. Standard for machines
with lots of RAM (i.e. PCs)
- EconoJIT
Produces uncached machine code which is not optimised. Lack of optimisation
and caches results in fast compilation. Standard for machines with little
RAM (e.g. devices running WindowsCE)
- PreJIT
Generates an EXE so really a normal compiler rather than a Just-In-Time
compiler. Standard when the initial compilation performance hit is
unacceptable (e.g. real-time applications where the code needs to be as fast
as possible from the start).
Note that the C# compiler generates and executable EXE containing byte code
rather that a java style standalone byte code.
Its not COM
.NET components are not registered in the registry. This information is held
in "manifests"
Data Types
bool |
double |
long (64-bits) |
byte |
float |
sbyte (8 bits) |
char (UNICODE, so 2
bytes) |
short (16-bits) |
struct |
decimal |
int (32-bits) |
ushort / uint / ulong (unsigned
versions of short, int & long) |
|
object (reference to
an object) |
string (reference to a
UNICODE string) |
|
?? I think of references as being like handles. Not sure if this is
technically accurate.
refVar2 = refVar copies the reference. There is still only one object. Note
that string references compare by value, not object.
Converting a value type to a reference type is called BOXING and involves
copying a the value type from the stack to the heap and returning a reference
to the heap version. We can't use the address of the value directly as when it
went out of scope the value on the stack would be lost yet we'd end up with a
pointer to an undefined value. The reverse
process is called UNBOXING.
In function calls, values are always passed by value, references by
reference (?? testing shows this is no so for strings ??). By reference can be
forced using the ref keyword.
Note the lack of a bit type (bit type in C++ was useful in strutures to say
"6 bits for day, 4 for month and x for year".
Object references can be compared using the Object.ReferencesEquals(ref1,
ref2) function.
Check for type and interface support using the is operator.
For example: if (o is System.String or o is IStringInterface)
The as command is like a cast except it returns null rather than a
cast exception is the cast is impossible. For example: IStringInterface oFred
= o as IStringInterface; if (oFred = = null) Console.Writeline("Uh Oh");
The type (Class) of an object can be returned using Type.GetType("className")
or typeof(className) [note the difference in quotes]. The returned result is a
Type object (i.e. not just a descriptive string) and this object can be use
to, for example, extract RTTI, call methods with an instance of this class,
get/set methods with an instance of this class, etc.
Enumeration
public enum myEnumName {red = 1, blue = 2, green = 3}
Arrays
Arrays are 0 based
Can be created pre-populated: int fred[] = {1,2,3,4};
Can be rectangular (where every row has the same number of elements):
int[,] fred; fred=new int[5,5];
or jagged (where each row can have a different number of elements): int[][]
fred; fred = new int[4][]; fred[1] = new int[2];
C/C++ vs C#
C# doesn't support macros (but retains conditionial
compilation) |
C# uses references instead of pointers, thus no ->
operator |
C# has automatic garbage collection. However, unlike C++,
you can't presume the destructor functions execute when the object is
dereferenced - you have to wait for the garbage collection to kick in. |
C# requires variables to be initialised before they can be
referenced (although if I try this in MS C++ I get a warning). The
obvious exception to this are function parameters declared with the out
keyword. |
C# does not have multiple inheritance of classes (multiple
inheritance of interfaces is fine). You can prevent a class being
inherited by declaring it with the sealed keyword |
C# only allows "fall through" in switch case
statements if the case contains no code, otherwise you have to use the
new construct: goto case constant (e.g. goto case 32) |
C++ long is 32-bits, C# long is 64-bits |
Cannot define default values for parameters in C#
(although you can simulate the effect by operator overloading) |
No global variables of methods in C# |
In C# 'if's require a result of type bool rather
than int |
C# supports foreach(type varName in collectionOrArray)
{...} |
C# case statements only support integer and string values |
C# sets class members to zero (or type equivalent)
automatically (presumably this is why structs cannot have parameter-less
constructors) |
Managed C++
Basically C++ code that runs in the CLR environment.
Using the Windows API
A two step process: Declaration and execution:
Declaration:
[sysimport(dll='user32.dll')]
private static extern int MessageBoxA(int hWnd, string
cMessage, string cCaption, int iTypeAndButtons);
?? doesn't work!
Execution is then like any other function call:
MessageBoxA(0, "test", "caption", 0);
Class Stuff:
- declared [scope] class className [:baseClass]
- When referring to a static member of a class you have to use the class
name as the prefix (in C++ you could also use the object name).
- The base keyword refers to the immediate parent (e.g.
base.SomeMethod();). Trying to stack base (e.g. base.base.SomeMethod() )
generates a compile time error.
- Parent classes can be referred to directly using the class name provided
they are declared static.
- The protected attribute restricts access to the class to itself and
its decendents.
- The internal attribute restricts access to classes within the same
assembly.
- protected and internal can be used together (as protected
internal) meaning that the attribute is restricted to this class and to
it's descends within the same assembly.
- The sealed attribute prevents sub-classing of the class. Methods
can be sealed (but only in sub-classes and only if the base class method is
declared virtual). Sealing a method does not prevent creation of a method
with the same name using the new attribute (?? is this a bug ??)
- ALL constructors for a class are always called (base constructors first,
sub-classed constructors last). It is possible to pass parameters given to a
sub-classed constructor up the chain as part of the sub-class constructor
function's declaration, for example: public l2(int p1, p2) : base(p1)
{...} would pass the p1 to the immediate parent of the class (if the
parent class had a parent class it would be up to it to pass the parameters
on if need be)
- Although it is not possible to edit the values given within the
constructor body and to pass those modified parameters to the parent class
it is possible to do this via (static) functions and expressions in the
constructor's header. For example: public l2(int p1, p2) :
base(myFunc(p1, p2) / 2) {...}
- To overload a constructor's parameters list use this instead of base.
For example:
public l1(int p1) {....} // Real
constructor
public l1():this(0) {} // Default value
constructor
- As additional safety thing, the virtual keyword must be used in the
declaration of any method of a class that needs to be resolved at run-time .
Sub-classes that override the virtual class have to use the override
keyword otherwise a compile error is generated.
- If a method of the same name is created in a subclass (without the parent
class' method being declared virtual) the parent method is said to be hidden
by the subclass' definition. If the new attribute is not used in
the sub-class definition the compile produces an error. If the sub-class is
cast to the parent class then the parent's method is called.
- Classes (and/or individual methods) can be declared using the abstract
attribute. Such classes cannot be instantiated and such methods must be
overridden in a sub-class. It is possible to declare variables of an
abstract type (e.g. absClass fred; fred = (absClass) jon;), just not to
create objects directly from an abstract type. The abstract attribute
automatically makes a method virtual so the virtual keyword is not
required.
- The syntax for operator overloading is (with the class definition): scope
static returnType operatoroperator (lhs, rhs) {...
return(result);}
Note that for operators that return a result a object this should be a
new object, not a modified version of lhs (?? is this a guideline or
a fact ??)
Example: public static daveTime operator+(daveTime daveLHS, daveTime
daveRHS) { daveTime tmpTime = new daveTime(daveLHS); tmpTime.minute +=
daveRHS.minute; if tmpTime.minute > 60 ..... return tmpTime;}
The += and -= operators cannot be overloaded directly. If you overload ==
you must also overload !=.
Implicit and explicit type conversions can also be overloaded but the syntax
is slightly different, e.g. public static opType operator toType(fromType
varName) {... return result;} where opType is explicit or
implicit, toType to the type we are being asked to convert to and
fromType the type we are converting from.
Interface Stuff
- Declared interface Iname {....} where the I in Iname is
standard rather than mandatory.
- Interfaces contain properties as well as methods. A property differs from
a member variable in that get/set methods are defined for it (i.e. its a
conceptual difference, not a syntaxical one)
- Methods in the interface do not have to be scoped as they as public by
definition (in fact, attempting to scope them to anything else results in a
compile time error).
- Properties can be public, private (if private, the get and set methods are
normally defined), virtual and / or static
- Property accessors are the only code allowed in an Interface and allow
indirect access to properties. This code is placed after the property
declaration. For example:
public int myVal {
get { return myVal; }
set { myVal = value; }
}
Note that value is a special keyword holding the value set by the
caller. Unlike VC++ or VFP++ the caller value is not passed in as a
parameter. The data type for value is determined by the property data
type (i.e. the compile prevents 'myVal = "string"' statements so
we don't have to write bucket loads of RTTI code in the set accessor).
- Property accessors for array indexes are called Indexers and have a
slightly different declaration and implementation:
public class DaveTest {
string[] myProp;
public void DaveTest {myProp =
{"Dave", "Was", "Here"};}
public string this[int index] {
get {
if index<0 or index > myProp.Length) throw new
ArgumentOutOfRangeException();
return myProp[index];
}
set {myProp[index]
= value; }
}
?? Such syntax seems to limit you to one indexer per class and that that
indexer occurs at the class level ??
- A class registers it intent to support an interface using the same syntax
as class inheritance (although unlike classes we can specify multiple
interfaces by separating them with commas). e.g. public class myclass :
Ione, Itwo, Ithree {...}
- A class must implement all the interface methods otherwise a compile time
error is generated.
- Example: interface IFfred {void method1(); void method2(int param1);
double var1;
- Since interfaces are so closely related to classes, objects can be cast to
an interface supported by that object's class.
- A tagging interface is an interface that contains no methods but instead
is used as a run-time test for behaviour. For example:
myObject.Save();
if (myObject is ICloseOnSave) Close();
Collections
- To make the foreach operator work with a class, that class must
implement the IEnumerable interface (which consists of a single
method definition IEnumerator GetEnumerator();) and the IEnumerator
interface (which does all the work).
- IEnumerator consists of:
- bool MoveNext() which should move to the next item in the
collection and return .f. when no more are found (note that MoveNext
moves the internal pointer and doesn't return any information about the
new item)
- object Current {get;} which should return the current item
- void Reset() which should reset the enumerator to its default
condition.
Delegates
- Type-safe version of function pointers. Syntax is scope delegate
fnDefintion for example public delegate int myFn(int a,
string b);
- Example of use:
class DaveTest {
public delegate int fred(int a,
int b);
public static int jon(int a, int
b) { return a+b;}
public static void Main() {
fred del
= new fred(jon);
Console.WriteLine(del(2,2));
}
Note: jon is declared static as the Main function is static (if we were not
getting the reference to jon in a static method we wouldn't have to declare
jon static. The compile presumable checks this because otherwise the static
Main function could "access" non-static properties of the class
via jon)
- Delegates can be stacked using the += operator. Since they all have the
same prototype the can all be passed the same parameters. If stacked
delegates return each return a value, its the value returned by the last
function added to the stack that is returned.
Events
- Events are closely linked to delegates. After declaring the delegate an
event can be declared in the class:
public class MyClassWithEvents {
public delegate void myEvent(int
someInfo);
public event myEvent OnMyEvent;
public myEventTriggerer() {
if
(OnMyEvent == null) System.Console.WriteString("Event not registered by
client!");
else
OnMyEvent(123);
}
}
public class myMain() {
public void myEventCallback(int
someInfo) {System.Console.WriteString("Event called!");
public void Main()
{
MyClassWithEvents mcwe = new MyClassWithEvents();
mcwe.OnRateChange += new MyClassWithEvents.myEvent(myEventCallback);
}
}
Note: This code doesn't actually do anything but set up and implement the
events
Neat Things
- Place an @ at the front of a string literal to prevent escape system being
processes (e.g. @"c:\temp\myfile.txt" is the equivalent to
"c:\\temp\\myfile.txt")
- In the command window you can use the alias command to assign menu
commands to text commands. A list of current assignments can be seen by
typing alias on its one. For example, alias quit File.Exit
allows you to enter quit in the command window to close VS.
- All code is in classes. There is no code outside of classes (code execute
starts with a public static function in any class (?? check) called Main.
Attempt to include multiple mains results in a compile error.
- Everything (including basic types such as int and char) derives from
System.Object. Thus everything has the following methods: ToString, Equals,
GetHashCode & GetType. This does mean you can do daft things
like: string silly = 101.ToString() !
- ToString accepts formatting, for example: (lSize/1024).ToString("n0")
+ "K"
- Variable argument lists (?? with variable types ??) are supported through
the params keyword, for example: public static void myFunc(params
object[] args) {...}. A function that uses the params keyword is matched
last so if an overloaded function that exactly matches the parameter list
can be found it will be used instead. Since all data types descend from object
the above function would accept any parameters, but it is perfectly valid to
be more discriminating, for example: public static Main(params string[]
args) {...}.
- Objects can be created dynamically (following example uses minimal
parameters and calls default construtor): object myNewObj =
Activator.CreateInstance(null, "DaveTest"); (the null is
passed in place of an assembly name to indicate DaveTest is in the current
assembly). Similarly methods can be invoked dynamically (i.e. from the
string form of the method name) using the InvokeMember method.
- Use of #region comment and #endregion to mark a collapsible
region in the editor
- Using reflection, methods and properties of objects can be referenced by
name. For example:
Employee fred = new Employee();
fred.m_strForename = "Fredrick";
// Get value of fred.m_strForename
Type myType = fred.GetType();
object oResult = (object) myType.InvokeMember("m_strForename",
BindingFlags.Default |
BindingFlags.GetField, null, fred,
new object [] {});
// Set value of fred.m_strForename
myType.InvokeMember("m_strForename", BindingFlags.Default |
BindingFlags.SetFields, null, fred,
new object [] {"Mr Fred"};
or more generically:
static object GetProp(object oObj,
string cFieldName) {
Type tType = oObj.GetType();
object oResult = (object)
myType.InvokeMember(cFieldName,
BindingFlags.Default | BindingFlags.GetField,
null, new
object[] {});
return oResults;
}
Error Handling
- All exceptions are from (or derive from System.Exception)
- try {... throw new System.Exception(["errorText"])...}
catch([System.Exception err]) {...}
- Exceptions are passed up the call stack until an try...catch construct is
found. If no such construct is found the program terminates.
- The stack property of the System.Exception class only display full source
code path and line number if the program is build in debug mode.
- finally clause is well implemented, running even if the user is
exiting the try {...} block with a return statement. Errors in finally
blocks are passed to the try{..} block.
- catch blocks must be in the correct order as a more general exception will
mask more specific ones. i.e. check System.Exception itself last.
- A expression (or function declaration) can be specifically marked as
"checked" or "unchecked", overriding the current
compiler setting for overflow checking. For example: checked(5*x); or
checked {... }
- throw on its own will pass the existing error up to the next
exception handler. This allows us to catch an error we think we can fix,
find out we can't fix it and so pass the error on up the chain.
- Unlike old implementations of C++ exceptions, C# exceptions do not incur
run-time overhead unless they thrown.
- Do not use exceptions as messages (e.g. EOF) as the overhead of processing
an exception is high.
Other Stuff:
- Like C++ return type does not play a part in function overloading
- Structs are designed to hold simple data structures and are as such value
datatypes (i.e. created on the stack and passed by values) rather than
reference datatypes.
- Enumerations are of type int by default, but this can be overridden - e.g:
enum DOW : short {Sunday, Monday, Tuesday, ... Friday};
- Because of the garbage collection issue, MS recommend implementing a
method called Close() if the resource can be reused (e.g. a file) or
Dispose() if the resource cannot. System.GC.Collect() can be used to request
a tidy up and System.GC.RequestFinializeOnShutdown() to request finalize
code be called for all objects on shutdown (default behaviour is not to call
any finalise code on shutdown in order to speed up program termination).
Shorthand:
- using (type varName = value){
commands
}
is shorthand for:
{
type varName = value;
try {
commands
} finally {
if (varName != null) ((IDisposable)
varName).Dispose();
}
}
- lock (expr) {commands}
is shorthand for:
System.Threading.Monitor.Enter(expr);
try {
commands
} finally {
System.Threading.Monitor.Exit(expr);
}
Format Strings:
Formatting strings have the general format {parameterIndex:formatCharacterXX}.
Any text outside of the curly braces is included verbatim. ParameterIndex
is a zero-based index for when the formatting string has to describe more than
one value (for example, "You are {0:D2} years old and you have {1:C} in the
bank"). FormatCharacter is one of:
Value is numeric (where shown, some codes are also valid for enumerations):
C |
D |
E |
F |
G |
N |
X |
Currency
(XX=number of decimal places (defaults to control panel values)) |
Decimal
(XX=width, left padded with 0s)Enums: Numeric version of enum |
Exponential
(XX = precision (in number of decimal digits)) |
Fixed
(XX = number of decimal places) |
General
(XX = precision (in number of decimal digits)Enums: String version of
enum |
Number
(1,234,567.12...) |
Hex
(XX=width, left padded with 0s)
Enums: Hex version of enum |
|
|
|
|
|
|
|
P |
R |
|
|
|
|
|
Percent
(XX=number of decimal places after conversion) |
Round-Trip
(XX = n/a) |
|
|
|
|
|
Note: FormatCharacter is not case sensitive except for "X" (Hex)
where the case of FormatCharacter is used to determine the case of the
resulting hex string.
Value is DataTime:
d |
D |
f |
F |
g |
G |
M (or m) |
Short date format
(culture specific) |
Long date format
(culture specific) |
Full format
(long date format + short time format, culture specific) |
Full format
(long date format + long time format, culture specific) |
General format
(short date + short time, culture specific) |
General format
(short date + long time, culture specific) |
Month/Day pattern
(e.g. MMMM dd, culture specific) |
|
|
|
|
|
|
|
R (or r) |
s |
t |
T |
u |
U |
Y (or y) |
RFC123 format
(e.g. ddd, dd MMM yyyy hh:mm:ss GMT) |
ISO 8601 format
(i.e. yyyy-MM-dd hh:mm:ss. Format is "sortable") |
Short time pattern
(e.g. HH:MM aa, culture specific) |
Long time pattern
(e.g. HH:MM:SS aa, culture specific) |
As per s, but using Universal
Time |
Universal format
(dddd, MMMM dd, yyyy hh:mm:ss aa) |
Year/Month pattern
(e.g. "MMMM, yyyy", culture specific) |
FormatCharacter can also be a picture string, using the following
values:
0 |
# |
. |
, |
% |
E+0 |
\char |
Digit placeholder (displayed as
zero if actual number smaller) |
Digit placeholder (not displayed
if actual number smaller) |
Decimal point
(culture specific) |
Numeric separator
(culture specific) |
Percent symbol
(culture specific) |
Exponential format |
Literals, such as \n |
|
|
|
|
|
|
|
; |
Everything Else |
d |
f (ff, fff etc) |
g (gg, etc) |
Other date/time |
t (tt, etc.) |
Section separator
(value is positive formatter; value is negative formatter; value is zero
formatter) |
Included verbatim |
d = day of month,
dd = " with leading zeros, ddd = abbreviated day name, dddd = full day name |
fractions of a second, to
varying number of d.p. |
period / era
?? = A.D? |
h /hh hour
m / mm minute
s / ss seconds
M / MM / MMM /MMMM month
y / yy / yyy year |
am/pm
z (zz, etc.)
Time Zone |
|
|
|
|
|
|
|
: |
/ |
|
|
|
|
|
Time Separator |
Date Separator |
|
|
|
|
|
Notes:
- {####} will display nothing if the value is 0. Suggest use {###0}instead.