A C# Cribsheet

James Haslam

Index

  1. Introduction
  2. Definitions
  3. Coding Standard
  4. Some Basic Concepts
  5. Casting
  6. Attributes
  7. Interfaces
  8. Operators
  9. Flow Control
  10. Exceptions and Error Handling
  11. Operator Overloading
  12. Delegates and Event Handlers
  13. Threads

Introduction

Here are some personal notes for the C# language and Microsoft's .NET framework.

The primary audience is myself. Those who are are already familiar with a similar language (such as Java or C++), or have seen the C# language before but have since forgotten it may also find this usefull. I will attempt to be as brief as possible with these notes, except in cases where I found the material to be tricky or different from both C++ and Java.

Definitions

Coding Standard

The best C# coding standard that I have found so far can be found on the Microsoft website. The document is called "Design Guidelines for Class Library Developers". The link to this document is HERE.

Some Basic Concepts

CTS Common Type System

The CTS is the scheme by which different components written in different languages can pass data to one another. In it, ALL types are objects, which are ultimately derived from System.Object. There are two types of objects, Reference types and ValueTypes. If you need to convert from one type to another (called "boxing/unboxing"), some unexpected results can happen.

Class Members Supported By the CTS

ValueTypes

The "primitive" types are called ValueTypes. Although they have some properties unique to them, they are ultimately still regarded as objects. All ValueTypes are derived from a special class called System.ValueType, which is in turn derived from System.Object

Reference Types

All other types not value types are reference types. They ultimately inherit from System.Object.

Casting

Employee e = new Employee();
ContractEmployee c = (Contract Employee)e;  // Runtime exception!

ContractEmployee c = e as ContractEmployee; // This version works, but c is null!
// The keyword "as" returns NULL for bad casts (as in this case)
// instead of throwing a bad cast runtime exception!

Attributes

As far as I know, the concept of attributes is not found in either C++ or java. I am therefore treating them in greater detail in this section. Attributes have the following properties:

Examples of appropriate use of attributes:

There are three types of attributes; Class Attributes, Method Attributes and Field Attributes. Attributes can have parameters and can have their usage defined via other attributes.

Class Attributes

As an example, say we wanted to define classes that send messages only to specific remote servers. We can use an attribute here to define if the class we are building is such a class, and if so, which server is it that we are sending info to.

Defining Class Attributes

Public enum RemoteServers
{
    PENDER,
	VALDEZ,
	SALTSPRING
}

// Attribute classes are named as "RemoteObjectAttribute"
// But but used as just as "RemoteObject".
// This is a conveniance offered by the compiler

Public class RemoteObjectAttribute : Attribute
{
    public RemoteObjectAttribute(RemoteServers Server)
	{
	    this.server = Server
	}
	protected RemoteServers server;
	Public string Server
	{
	    get
		{
		    // note the interesting use of reflection here!
		    return RemoteServers.GetName(typeof(RemoteServert), this.server);
		}
	}
}

Using Class Attributes

After the attribute has been defined, we may now use it on a class. To define a class that works only on a remote server now, we may use the attribute as such:

[RemoteObject(RemoteServers.PENDER)]
class MyRemotableClass
{
   ...
}

Querying Class Attributes

To query for an attribute, we need to use reflection. For the different types of attributes, this reflection code is different. For class attributes it is as such:

...
Type type = typeof(MyRemotableClass);
foreach (Attribute attr in type.GetCustomAttributes())
{
    RemoteObjectAttribute remoteAttr = attr as RemoteObjectAttribute
	if (null != remoteAttr) // Clever! detects if this attribute is a RemoteObject!
	{
	    Console.WriteLine("Create This Object on {0}.", RemoteAttr.Server);
	}
}

Method Attributes

Methods can be tagged with an attribute instead of whole classes, but the reflection code to query the method attribute is slightly different.

Defining Method Attributes

The attribute has no parameters in this example. The attrribute's presence alone will be enough.

public class TransactionableAttribute : Attribute
{
    public TransactionableAttribute()
    {
    }
}

Using Method Attributes

public class TestClass
{
    [Transactionable]
	public void Foo()
	{
	}

	public void Bar()
	{
	}

	[Transactionable]
	public void Baz()
	{
	}
}

Querying Method Attributes

Note the reflection code is different here.

Type type = Type.GetType("TestClass");
foreach (MethodInfo method in type.GetMethods());
{
    foreach (attr in method.GetCustomAttributes())
	{
	    if (attr is TransactionableAttribute)
		{
		    Console.WriteLine("{0} is transactionable.", method.Name);
		}
	}
}

Field Attributes

Defining and using field attributes are the same as the method and class cases. Just querying is different.

Querying Field Attributes

Type type = Type.GetClass("TestClass);
foreach (FeildInfo field in type.GetFields())
{
    foreach(Attribute attr in field.GetCustomAttributes())
	{
	    RegistryKeyAttribute registryKeyAttribute = attr as RegistryKeyAttribute;
		if (null != registryKeyAttribute)
		{
		    Console.WriteLine("{0} will be saved in {1}\\\\{2}",
			   field.Name, RegistryKeyAttribute.Hive, RegistryKeyAttribute.ValueName);
		}
	}
}

Attribute Parameters

[RegistryKey(RegistryHives.HKEY_CURRENT_USER, "foo")]

Parameters are the things between the brackets.
There are two types of parameters: Positional Parameters and Named Parameters
Valid attribute parameter types: bool, byte, char, double, float, int, short, string, System.Type, Object, enum, and 1-dimensional arrays.

Positional Parameters

Positional parameters are defined in the attribute's CONSTRUCTOR. They must be specified every time an attribute is defined.
example:

[RegistryKey("foo")]

Named Parameters

Named parameters are NOT defined in the constructor, but are rather non-static fields & properties in an attributes definition.
example of using:

[RegistryKey("Foo", Hive = RegistryHives.HKEY_LOCAL_MACHINE)]

Only after all positional parameters are defined can we define the (optional) named parameters.
example of defining:

public enum RegistryHives
{
    HKEY_CLASSES_ROOT = 1; // this is done because "undefined" defaults to zero
	HKEY_CURRENT_USER      // this way, we can distinguish between initialized and not
}
...
public class RegistryKeyAttribute : Attribute
{
    public RegistryKeyAttribute(String ValueName)
	{
	    // This makes a default hive in the constructor to be something meaningfull
	    if (this.Hive = 0)
		    this.Hive = RegistryHives.HKEY_CURRENT_USER;
		this.ValueName = ValueName;
	}
}

Attribute Usage

An attribute definition can also have it's own class attribute... In the form of an "Attribute Usage" attribute. This tag indicates how and where an attribute is availiable for use. A template for the tag is as follows:

[AttributeUsage(
    validon,
	AllowMultiple = allowmultiple,
	inherited = inherited
)]

Valid On

This field indicates what classes, fields, methods, etc. that the attribute being defined can be used with. (Ie. can it ONLY be used to define classes, or can we use it with methods too?) The validon parameter is positional, and thus is manditory when defining an attribute. An attribute's default is ALL, however, when no attribute usage attribute is defined. Example of use:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class RemoteObjectAttribute : Attribute
{
...
}

Valid targets are: Assembly, Module, Class, Struct, Enum, Constructor, Method, Property, Field, Event, Interface, Parameter, Delegate,
All (the default, which is all the above put or'ed together),
Class Members (which is some of the above or'ed together)

Single Use / Multi Use

Allows / Disallows an attribute to be defined multiple times on the same target.
For example:

[AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
public class TransactionableAttribute : Attribute
{...}
...
[Transactionable(1)]
[Transactionable(2)] // This would result in an error
public SomeMethod()
{
}

Inheritance

Attribute Identifiers

Determines WHAT you want to annotate.
For example, if we want to annotate the RETURN value rather than a method, we can do this:

[return: Transactionable]
public long foo()
{
...
}

For the most part, the compiler figures out (via defaults) what is the most likely thing the programmer wants to do.
Possible identifiers are: assembly, module, type, method, property, event, field, param, return.

Interfaces

Declaring

interface IExampleInterface
{
    //Example Property Declaration
	int testProperty {get;}
	
	//Example Event Declaration
	event testEvent Changed;

	//Example Indexer Declaration
	string this [int index] {get; set;}

	//Example Method Declaration
	bool Validate();
}

Implementing

class MyControl : Fancy Control, IValidate
{
    public MyControl()
	{
	    data = "My Grid Data";
	}

	public bool Validate()
	{
	    Console.Writeline("Validating ... {0}", data);
		return true;
	}
}

Using

...
MyControl myControl = new MyControl();
IValidate val = (IValidate)myControl;
bool success = val.Validate();
...

Querying for Implementation using 'is'

...
MyControl myControl = new MyControl();
if (myControl is ISerializable)   // Avoids invalid cast runtime errors
{
  ISerializable ser = (ISerializable)MyControl;
}
...

Querying for Implementation using 'as'

...
My Control myControl = new MyControl();
ISerializable ser = myControl as ISerializable;
if (null != ser)
{
    // do stuff
}
...

Name Hiding

You don't have to cast the object to its implemented interface to call that interface's methods. For example:

EditBox edit = new EditBox();
edit.Bind();       // use of interface without casting
IDataBound bind = (IDataBound)bind;
bind.Bind()        // use of interface with casting

Avoiding Name Ambiguity

If two interfaces have the same member, we deal with the ambiguity in the following way:

interface ISerializable
{
  void SaveData();
}

initerface IDataStore
{
    void SaveData();   // This name is same as ISerializable's version
}

Implementing

class Test : ISerializable, IDataStore
{
    void ISerializable.SaveData() {..}
	void IDataStore.SaveData() {..}
}

Using

Test test = newTest();
((ISerializable)test).SaveData();
((IDataStore))test.SaveData();

Interfaces & Inheritance

Potential Problems:

  1. Base class contains a method name identical to the name of an interface method.
  2. A derived class has the same name as the base class implementation of an interface method

Solution:

  1. Always cast the object to the interface whose member you're attempting to use
  2. When you cast an object to an interface, the compiler traverses the inheritance tree until a class is found that contains the interface in its base class.

Combining Interfaces

Combines two or moer interfaces such that the class need only implement the combined result

public interface ICombo: IDragDrop, I Serialize
{
    //We can add more here if we like
}

Operators

I will only touch on the important/novel operators in this section

typeof

Works with System.Type .NET class

using System.Reflection;

Type t = typeof(Apple);
string className = t.ToString();
MethodInfo[] methods = t.GetMethods();
foreach(MethodInfo method in methods)
{
   Console.WriteLine(method.ToString());
}

MemberInfo[] allMembers = t.getMembers();
foreach (MemberInfo member in allMembers)
{
    Console.Writeline(member.ToString());
}

sizeof

struct MyStruct
{
    int i;
	int j;
}

static unsafe public void ShowSize()
{
    Console.WriteLine(sizeof(short));
	Console.WriteLine(sizeof(MyStruct));
}

Comparison Operators

Assignment

Foo test1 = new Foo(1);
Foo test2 = new Foo(2);
test1 = test2;
test1.i = 42;
Console.WriteLine("{0}", test2.i);   // returns 42!

Flow Control

if

Expressions must be boolean (unlike c++ where it can be an int or null)

if (expresssion)
    STATEMENT
else if (expression)
    STATEMENT
else
    STATEMENT

switch

Switch statements work a bit different in C# then in C++ or java.
EXPRESSION can resolve to type: sbyte, byte, short, ushort, int, uint, long, ulong, char, string or an enum based on one of these types (so long as it's cast to one of the above types within the expression)

switch(EXPRESSION)
{
    case(CONSTANT_EXPRESSION):
        STATEMENTS;
        break;
    case(CONSTANT_EXPRESSION):
        STATEMENTS;
        break;
    default:
        STATEMENTS;
        break;
}

C# does not allow fallthroughs (unlike C++ and Java), but it does support "combining case lables" such as the following:

switch((int)this.tender)
{
    case (int)Tenders.Cash:
        //dostuff;
        break;
    case (int)Tenders.Visa:
    case (int)Tenders.Mastercard:
    case (int)Tenders.Amex:
        //domorestuff;
        break;
}

Finally, the goto statement is also useable in a switch statement

switch((int)this.tender)
{
    case (int)Tenders.ChargeOff:
        //dostuff;
        goto case (int)Tenders.Cash;
    case (int)Tenders.Diners:
        //dostuff;
        break;
    case (int)Tenders.Cash:
        //dostuff;
        goto default;
    case (int)Tenders.Visa:
    case (int)Tenders.Mastercard:
    case (int)Tenders.Amex:
        //domorestuff;
        break;
    default:
        //domorestuff
        break;
}

do/while

do
{
    //statements;
} while (BOOLEAN-EXPRESSION);

for

Unlike c++, the scope of the variable i is restricted to the inner scope.

// outer scope
for (int i=0; i<10; i++)
{
    //inner scope
}

// multiple variable example
for (int i = start, j=1; i <= end; i++, j++)
{
    //inner scope
}

foreach

foreach(string word in myarray.words)
{
    //do stuff
}

Exceptions and Error Handling

Throwing

public void SomeMethod()
{
    // Some error detected
    throw new Exception();
	// or throw new Exception("Thrown my SomeMethod");
}

Catching

public void Foo()
{
    try
    {
        Bar();
    }
    catch (System.Exception e)
    {
        Console.Writeline(e.Message);
    }
}

Rethrowing and exception

An exception should not be simply rethrown without doing some cleanup, otherwise there is little point!

...
try
{
    Bar();
}
catch (Exception e)
{
    // Free some resources
    throw;
}
...

Finally

Finally ensures that a piece of code is always run, regardless of wether an exception is caught. For example, if an exception is thrown after a resource is allocated - We must deallocate the resource in the finally block

...
// allocate resources
try
{
    // do stuff that could throw
}
catch (Exception e)
{
   // exception handling
}
finally
{
   // cleanup resources
}
...

Throwing Exceptions From Constructors

Throwing an exception from a constructor is usefull because a constructor can't return anything!

try
{
    AccessDatabase accessDB = new AccessDatabase();
}
catch (Exception e)
{
   // Inspect caught exception;
}

The 4 Ways to Construct an Exception

  1. public Exception();
    throw new Exception();
  2. public Exception(String)
    throw new Exception("Dammit, jim!");
    ...
    catch (System.Exception e)
    {
        Console.WriteLine(e.Message); }
  3. proteted Exception(SerializationInfo, Streaming Context);
    Initializes exception with serialized data.
  4. public Exception( String, Exception ); ...
    throw new Exception(" ", new FormatException "Invalid Parameter Specified");
    ...
    catch(Exception e)
    {
        Exception inner = e.InnerException;
        Console.WriteLine(e.Message);
    }

The Stack Trace Property

Catching Multiple Exceptions

We can add a catch block for each type of exception that your code needs to handle. Note in the example below how we must handle derived exception classes first before the base classes. Otherwise, the derived blocks would be unreachable!

try
{
    Foo();  // Can throw FooException;
    Bar();  // Can throw BarException;
}
catch (FooException e)
{
    Console.WriteLine(e.Message);
}
catch (BarException e)
{
    Console.WriteLine(e.Message);
}
catch (Exception e)
{
    Console.WriteLine("General Exception");
}

Deriving Your Own Exception Classes

Reasons:

public class TestException:Exception
{
    public TextException() : base() {}
    public TextException(String message) : base(message) {}
    public TestException(String message, Exception innerException) : base(message, innerException) {}
    // Add custom methods here
}

Good practice:

Operator Overloading

public static Invoice operator+ (Invoice invoice1, Invoice invoice2)
{
    // create a new invoice object obj
    // combine contents into obj
    // return obj
}

Overloadable operators

User-Defined Conversions

This is poorly treated right now. I will have to return to it. Basically, a user-defined conversion allows structs or classes to be converted "automatically" to other structs & classes by way of simple assignment statements. This is done in a manner that seems a lot like overloading a "cast" operator.
Example of using two user-defined conversions are:

Farenheit f = 98.6F;      //Constructor conversion
Celcius C = (celcius)f;   //Casting operator conversion

The two basic forms are:

A user-defined conversion can be used in two ways:

As a constructor
struct Celcius
{
    ...
    public static implicit operator Celcius (float temp)
    {
        Celcius c;
        c = new Celcius(temp);
        return(c);
    }
}
// usage: Celcius mytemp = 37.5F;
As casting operator:
struct celcius
{
    ...
    public static implicit operator float(Celcius c)
    {
        return((((c.temp - 32) / 9) * 5 ));
    }
}
// usage: float myTemp = myCelcius;

Delegates and Event Handlers

Delegates as Callbacks

This is a similar concept to callbacks in other languages. Basically, it is a way of passing a function to another function via an itermediate container. The most quoted uses of this are:

Defining a Delegate:

It when not used as events (see later) is customary end deligate names with "Callback".

class Widget
{
    private float myID;
    ...
	
    // This is like a "method signature", or header.
    // All callback functions must conform to this delegate definition
	// to be useable!
    public delegate void WidgetCallback(float widgetID);

    // This method definition accepts a delegate as a parameter
    public void ExecuteCallback(WidgetCallback aCallback)
    {
        ...
        // "aCallback" accepts a float, as defined by the delegate definition!
        aCallback(myID); // this actually executed the callback function!
    }
}

Using a Delegate

To use this defined delegate:

class app
{
    ...

    // This is the function to eventually be called as a function pointer
    public static void ButtonPressFunction(float buttonNumber)
    {
        Console.Writeline("Button {0} pressed.", someNumber);
    }
	
    public static void Main()
    {
        ...

        // The delegate is instantiated here, with the actual Callback Handler
        // which is defined immediately above
	    Widget.WidgetCallback someCallback = new Widget.WidgetCallback(ButtonPressFunction);

		...

		MyWidget.ExecuteCallback(someCallback);
    }

}

Some Delegate Tricks

A better way is to instantiate the delegate as a static member:

class app
{
    ...

    //Instantiate the delegate as a static member!
	public static Widget.WidgetCallback buttonPressCallback = new Widget.WidgetCallback(ButtonPressFunction);
    public static void ButtonPressFunction(float buttonNumber)
    {
        Console.Writeline("Button {0} pressed.", someNumber);
    }
	
    public static void Main()
    {
        ...
		MyWidget.ExecuteCallback(buttonPressCallback);
    }

}

Sometimes, instantiating a delegate can be expensive, so it sometimes makes sense to do it lazily, by using a property:

class app
{
    ...

    //Instantiate the delegate via a property
	public static Widget.WidgetCallback buttonPressCallback 
	{
        get
        {
            return new Widget.WidgetCallback(buttonPressCallback);
        }
    }

    //...(define appCallback here)

    public static void Main()
    {
        ...
		MyWidget.ExecuteCallback(app.buttonPressCallback);
    }
}
Arrays of delegates can be made. This is usefull for:

Usage:

MyCallback[] delegateArray;              // declare an array of delegates
delegateArray = new MyCallback[10];      // creates space for 10 delegates
delegateArray[6] = someDelegate;         // adds a delegate to the array
...
delegateArray[6]();                      // Invokes the callback

Composite (Multicast) Delegates

Underneath the hood, any delegate that returns void is a multicast delegate; a linked list of delegates. This "linked list" understands the operators + and - and may be used in two ways:
Say we have two callback functions, appHandler1 and appHandler2. In the above Widget example, more than one callback can be executed, by simply using the delegate as such:

...
public static void Main()
{
    Widget.WidgetCallback myCallback1 = 
        new Widget.WidgetCallback(appHandler1);
    Widget.WidgetCallback myCallback2 = 
        new Widget.WidgetCallback(appHandler2);

    Widget.WidgetCallback myCallback = myCallback1 + myCallback2;
	//or
	myCallback1 += myCallback2;
    //note: -= works as well!

    MyWidget.ExecuteCallback(myCallback);
}

Iterating Through Composite Delegates

The individual delegates in the linked list are called sequentially, in the order in which they were inserted onto the list (via + or +=). When the delegate is "Invoked" (callback called) then ALL delegates in the linked list are automatically called! In our example, aCallback(myID); calls all callbacks registered!

In the case where the delegate has a return value, and we want to make use of that return value, then we may want to manually iterate through all the targets, This piece of code does the trick (note: to be used ONLY when the delegate has a return value):

if ( allCallbacks != null)
{
    foreach (Widget.ButtonPressCallback bpCallback in allCallbacks.GetInvocationList())
    {
        accumulator += bpCalback();
    }
}

Events

The publish-subscribe design pattern is explicitly supported in C# by way of events: a class publishes an event that it can "raise". Any number of classes can then subscribe to the. Events are the formalized use of (multicast) delegates that support public [un]registration and private invocation. I look at them like a "container" for delegates, with a little more interface control.

Yes, we can technically do everything using just delegates but using the event interface, but by simply adding the "event" keyword we get:

Publishing an Event

This is similar conceptually to declaring a delegate instance variable. They keyword "event" prefixing the instance declaration is the only thing that is different on the implementation side. Events "hold" delegates. By convention (read: it doesn't have to, but it is a recommended design pattern) events have the pattern described below.

Note:
class WidgetEventArgs : EventArgs
{
    public ButtonPressEventArgs(int id)
    {
        this.id = id;
    }
    public int id;
}

//The publisher
class Widget
{
    ...
    public delegate void ButtonPressHandler (object source, ButtonPressEventArgs e);
    public event ButtonPressHandler OnButtonPress;
}

Subscribing to an Event

This is as we have seen before with standard delegates, (used as instance variables) except: assignment to event field disallowed (to avoid clobbering existing delegates) and execution of event not supported (must be in class only!)
Example of proper use:

myWidget.OnButtonPress += new myWidget.ButtonPressHandler(someFunction);
myWidget.OnButtonPress -= someDelegate;

...

public void someFunction(object source, ButtonPressEventArgs e);
{
    //do stuff
}

Customizing Event [Un]Registration

If we need to do more than just register another delegate when a client uses the+= and -= forms, we can do like this:

class Widget
{
    ...
    public event ButtonPressHandler OnButtonPress{
        add
        {
            counter += 1;  // or whatever else you want
            _myEventDelegateContainer += value;
        }
        remove
        {
            counter -= 1;
            _myEventDelegateContainer -= value;
        }
    }
}

Threads

Threads are a way to concurrently execute different parts of a program at once. Carefull use of threads can increase a program's responsiveness and throughput. Careless use winds up with reduced throughput and inconsistent bad behaviour which is difficult to debug.

Managing Threads

We will examine creating, killing and joining threads here. All creation and management of threads is done through the System.Threading.Thread class.

Creating and Starting Threads

To create a thread, create an instance of the Thread class. The thread class constructor takes a delegate as an arguement. The CLR provides the ThreadStart delegate specifically for this purpose.
Example:

Using System;
Using System.Threading;
...

public void ConcurrentFunction()
{
    //do stuff concurrently
}
...
// Create the threads here!

ThreadStart worker = new ThreadStart(ConcurrentFunction);
Thread t1 = new Thread(worker);
//Better yet
Thread t2 = new Thread( new ThreadStart (ConcurrentFunction) );


// Start the threads here!
t1.Start();
t2.Start();

Killing Threads

Normally, threads die after running their course. You can ask a running thread to kill itself by calling either its Interrupt() or Abort() methods.

Interrupting a thread causes it to throw a ThreadInterruptedException, which the thread can be caught in order to clean up any allocated resources.

Aborting a thread causes it to throw a ThreadAbortException. In this case however, even if the method attempts to catch the ThreadAbortException, the runtime won't let it. However, the thread's finally block will still get executed, if present. Once you abort a thread, it cannot be restarted. In that case, although you have a valid Thread object, you can't do anything usefull with in in terms of executing code.

example:

...
public void ConcurrentFunction()
{
    try
    {
        //do stuff concurrently
    }
    catch(ThreadAbortException e)
    {
        //This block never gets executed.  CLR won't let it
    }
    catch(ThreadInterruptedException e)
    {
        // Free Resources if thread is interrupted.
    }
    finally
    {
        // Free resources in either case
    }
}
...

Thread t1 = new Thread( new ThreadStart (ConcurrentFunction) );
t1.Start();
...

t1.Interrupt();
t2.Abort();

Pausing Threads

A thread can put itself to sleep for by a short time by calling the Thread.Sleep method as such:

Using System.Threading;
...
Thread.Sleep(100);

The parameter is the time in miliseconds to sleep. It can be passed as an integer or a TimeSpan object. If you specify zero, then the thread simply gives up the remainder of its timeslice. Note that you are not allowed to call Thread.Sleep on any other thread other than the currently executing one. That's why Thread.Sleep is a static method.

A thread can pause another thead using the Thread.Suspend method. It is used as such:

Joining Threads

t1.Join() method halts the current thread that the method was called on, and waits for the thread t1 to complete.
To join thread t1 (onto the end of) to t2: t2.join();
Here are some good uses:

// Wait for all child threads to complete
foreach (Thread myThread in myThreads)
{
    myThread.Join();
}
Console.Writeline("All my threads are done.");

// Makes sure a thread is dead before continuing
myThread.Interrupt();
myThread.Join();
Console.Writeline("Thread interrupted!");

Scheduling Threads

Threads can be prioritized into five different levels using the Thread.Priority property. The priorities that you can set this property are defined by an enum as follows:

For example:

 t1.Priority = ThreadPriority.Highest;
 t2.Priority = ThreadPriority.Lowest;
 

The default priority is, of course, Normal. Several threads running at the same priority get round robin scheduling. Be carefull when prioritizing some threads really high, as it may cause the GUI to become sluggish.

Thread Synchronization

There are times in which we will want to control access to a resource, such as a shared object, so that only one thread at a time can modify and use that resource. The four synchronization mechanisms we will examine: The Interlock class, the C# Lock statement, the Monitor class and the Mutex class.

Interlocks

There some tasks that tasks that need synchronizing protection and seem to arise so frequently that the .NET libraries include specific support for them. The Interlocked class is meant to handle such functions. Incrementing and Decrementing are some such common tasks. Here's an example of Interlocked in action.

int count = 0;
...

public void DoStuffInAnotherThread()
{
    Interlocked.Increment(ref counter);
    ...
} 

In this case, Interlocked.Increment() expects a sincle parameter: a reference to an int. Because int values are passed by value, you use the ref keyword. More importantly, the increment is done in a thread safe manner!

Locks

A lock marks a critical section of your code, providing synchronization to an object you designate while the lock is in effect. It is a simpler form of a Monitor, which is discussed later.

class Database
{
    public void SaveData(string text)
    {
        lock(this)
        {
            //open database
            //write database
            //comitt database
            //close database
        }
    }
}

Monitors

The Monitor class takes synchronization one step further than locks for more sophisticated control.

class Database
{
    public void SaveData(string text)
    {
        Monitor.Enter(this)
		
        //open database
        //write database
        //comitt database
        //close database
		
        Monitor.Exit(this)
    }
}

Monitor.Enter(Object) and Monitor.Exit(Object) mark entry into, and exit from the critical section.

Suspending Threads using Monitors

In the middle of a monitor, if the command Monitor.Wait(this) is called then the thread suspends (and frees the monitor) until another thread makes a call to Monitor.Pulse(this). Once Pulse() is called, then the suspended thread re-enters the critical section, continuing on from where it called Wait().
In this scheme that threads can only suspend themselves!

Monitor.Wait(this) and Monitor.Pulse(this), must BOTH be called within a "synchronized block", ie- they must be bound by Monitor.Enter(this) and Monitor.Exit(this) statements.

Mutexes

The Mutex class is a runtime representation of the Win32 primitive. They are a lot slower but have increased flexibility over what we have seen. A mutex is an object with these possible constructors:

Mutex mutex = new Mutex();   //Current thread is owner of mutex
Mutex mutex = new Mutex(false);   // Does the thread creating the mutex want to own (lock) it?
Mutex mutex = new Mutex(false, "My Mutex");   // As above but with name assigned