Sunday, September 28, 2008

C# 2.0 - Anonymous Methods

In the program below, we have used "named methods".


class Program {

static void Main(string[] args) {
Fn f = Add; // Fn f = new Fn(Add);
Console.WriteLine(f(20, 30));
}

static int Add(int a, int b) {
return a + b;
}

delegate int Fn(int a, int b);
}



The same can be written using anonymous methods (introduced in c# 2.0) as


class Program {
static void Main(string[] args) {

Fn f1 = delegate(int a, int b) {
return a + b;
};

Console.WriteLine(f1(50, 60));
}

static int Add(int a, int b) {
return a + b;
}

delegate int Fn(int a, int b);
}



The advantages of using anonymous methods is

1. Elegant and clean code.

2. The place where anonymous methods come handy is that it includes capability of capturing local state. The local variables and parameters whose scope contain an anonymous method declaration are called outer or captured variables of the anonymous method. For example, in the following code segment, n is an outer variable:

int n = 0;
Del d = delegate() { System.Console.WriteLine("Copy #:{0}", ++n); };

3. Unlike local variables, the lifetime of the outer variable extends until the delegates that reference the anonymous methods are eligible for garbage collection. A reference to n is captured at the time the delegate is created.

4. An anonymous method cannot access the ref or out parameters of an outer scope.

Another example

List list = new List() { 50, 20, 30 };
list.Sort(delegate(int a, int b) { return a - b; });

Friday, September 26, 2008

Threads - Thread Synchronization

1. Exclusive locking using Monitors

lock(obj){

}

is similar to

Monitor.Enter(obj)



try{
.....
}

finally{
Monitor.Exit(obj)
}



It is considered a better practice to lock on an object internal to your class rather than on the class itself, which is externally exposed.

Limitation of Monitors

1. They only provide synchronization based on exclusive locking. In other words, a monitor cannot permit access to more than one thread at a time. This is true even when a set of threads are only attempting to perform read operations. This limitation can lead to inefficiencies in a scenario in which there are many read operations for each write operation.

1. Shared locking using ReaderWriterLock

The ReaderWriterLock class allows you to design a synchronization scheme which employs shared locks together with exclusive locks. This makes it possible to provide access to multiple reader threads at the same time, which effectively reduces the level of blocking. The ReaderWriterLock class also provides exclusive locking for write operations so you can eliminate inconsistent reads.

a. The reading thread can acquire a shared lock using method AcquireReaderLock

b. The writing thread can acquire an exclusibe lock using method AcquireWriterLock . Once an exclusive lock is acquired, all other reader threads and writer threads will be blocked until this thread can complete its work and call ReleaseWriterLock.

c. What would happen if a single thread calls AcquireWriterLock more than once before calling ReleaseWriterLock? Your intuition might tell you that this thread will acquire an exclusive lock with the first call to AcquireWriterLock and then block on the second call. Fortunately, this is not the case.
The ReaderWriterLock class is smart enough to associate exclusive locks with threads and track an internal lock count. Therefore, multiple calls to AcquireWriterLock will not result in a deadlock. However, this issue still requires your attention because you must ensure that two calls to the AcquireWriterLock method from a single thread are offset by two calls to the ReleaseWriterLock method. If you call AcquireWriterLock twice and only call ReleaseWriterLock once, you haven't released the lock yet.

d. Note that it is not possible for any single thread to hold both a shared lock and an exclusive lock at the same time. However, it is not uncommon that a thread will need to obtain a shared lock at first and then later escalate to an exclusive lock to perform write operations. The key point about using the ReaderWriterLock class is that a thread should never call AcquireReaderLock and then follow that with a call to AcquireWriterLock. If you do this, your call to AcquireWriterLock will block indefinitely. Instead, after calling AcquireReaderLock you should call UpgradeToWriterLock to escalate from a shared lock to an exclusive lock, as shown here:

// Acquire shared lock
lock.AcquireReaderLock(Timeout.Infinite)

// Escalate shared lock to exclusive lock
lock.UpgradeToWriterLock(Timeout.Infinite)

The key point is that a call to UpgradeToWriterLock doesn't lock down your data.

Summary of

http://msdn.microsoft.com/en-us/magazine/cc188722.aspx
http://msdn.microsoft.com/en-us/magazine/cc163846.aspx

Threads - Secondary Threads

1. A native thread is a Win32 thread which created by Windows OS where as a managed thread is a thread created by .NET Framework.

2. But is there a difference... after all a managed thread would be a simple wrapper around Win32 thread ?

a. Yes, there is a difference. With version 1.0 and version 1.1 of the CLR, each Thread object is associated with its own physical Win32® thread. Note that future versions of the CLR are likely to provide an optimization whereby it will not be necessary to create a separate physical thread for each Thread object.

b. And creating an object from the Thread class doesn't actually create a physical thread. Instead, you must call the Thread object's Start method for the CLR to call to the Windows OS and create a physical thread.

3. There is an overhead involved in creating and destroying physical threads.

4. The lifetime of this physical thread is controlled by the target method's execution. When execution of the method completes, the CLR gives control of the physical thread back to Windows. At this point, the OS destroys the physical thread.

Creating secondary thread

1. The Thread class is used to create secondary threads.

2. It takes an object of ThreadStart delegate

public delegate void ThreadStart();

3. To pass parameters to thread you have two options

a. Use ParameterizedThreadStart

b. Create a custom thread class with a parameterized constructor. When you create an object from a custom thread class, you can initialize it with whatever parameter values are required in your particular situation.



class Program {
static void Main(string[] args) {

ThreadStart st = new ThreadStart(new A().ThreadMethod);
System.Threading.Thread th = new System.Threading.Thread(st);
th.IsBackground = true;

th.Start();
}

}

class A {

public void ThreadMethod() {
Thread.Sleep(-1);
}
}


Lifecycle of a Thread



1. When you create a thread and have not called the Start method, the state of the thread is Unstarted.

2. After you call the Start method,

a. If the thread is a background thread the state is Background
b. If the thread is a foreground thread the state is Running
c. If the thread is sleeping, the state is WaitSleepJoin

3. A thread in WaitSleepJoin is also called blocked thread i.e. it waits or pauses for a result. Once blocked, a thread immediately relinquishes its allocation of CPU time and doesn’t get re-scheduled until unblocked. A thread, while blocked, doesn't consume CPU resources. A thread can enter blocked by calling
a. Thread.Sleep
b. Thread.Join
c. Thread Synchronization Blocking (Lock, Mutex, Semaphore) or Signaling Constructs (WaitHandles)

Thread Methods

1. public void Join();
2. public bool Join(int millisecondsTimeout);

Blocks the calling thread until a thread terminates or the specified time elapses, while continuing to perform standard COM and SendMessage pumping.

3. public void Abort();

4. public void Interrupt();

5. The Resume and Suspend methods have been suspended.

Interrupts a thread that is in the WaitSleepJoin thread state.

Handling Exceptions

1. Exceptions from secondary threads are never passed to other threads i.e. each thread should handle its own exceptions.

2. From .NET 2.0 onwards, an unhandled exception on any thread shuts down the whole application, meaning ignoring the exception is generally not an option. Hence a try/catch block is required in every thread entry method – at least in production applications – in order to avoid unwanted application shutdown in case of an unhandled exception.

3. Application.ThreadException which is global exception handler in Windows Forms applications do not get exceptions raised by worker threads. The .NET framework provides a lower-level event for global exception handling: AppDomain.UnhandledException. This event fires when there's an unhandled exception in any thread, and in any type of application (with or without a user interface).

When to create a secondary thread

You should be creating and managing secondary threads to execute methods asynchronously only when you cannot use delegates.

1. You need to execute a long-running task

When you need to dedicate a thread to a task that's going to take a long time, you should create a new thread. It would be considered bad style to use delegates because you would effectively be taking a thread out of the CLR thread pool and never returning it. Asynchronous method execution using delegates should only be used for relatively short-running tasks.

2. You need to adjust a thread's priority

3. You need a foreground thread that will keep a managed desktop application alive

Each Thread object has an IsBackground property that indicates whether it is a background thread. All the threads in the CLR thread pool are background threads and should not be modified in this respect. That means a task executing asynchronously as a result of a call to BeginInvoke on a delegate is never important enough by itself to keep an application alive.

4. You need a single-threaded apartment (STA) thread to work with apartment-threaded COM objects.

5. Use a dedicated thread if you want to abort it prematurely by calling
Thread's Abort method.

Saturday, September 20, 2008

Messages and Message Queues

Summary of link http://msdn.microsoft.com/en-us/library/ms644927(VS.85).aspx

There are two kinds of messages

1. Queued Messages

a. A message queue is a first-in, first-out queue, a system-defined memory object that temporarily stores messages, and sending messages directly to a window procedure.

b. Messages posted to a message queue are called queued messages. They are primarily the result of user input entered through the mouse or keyboard. Other queued messages include the timer, paint, and quit messages.

c. The system maintains a single system message queue and one thread-specific message queue for each graphical user interface (GUI) thread. To avoid the overhead of creating a message queue for non–GUI threads, all threads are created initially without a message queue. The system creates a thread-specific message queue only when the thread makes its first call to one of the User or Windows Graphics Device Interface (GDI) functions.

d. Whenever the user moves the mouse, clicks the mouse buttons, or types on the keyboard, the device driver for the mouse or keyboard converts the input into messages and places them in the system message queue. The system removes the messages, one at a time, from the system message queue, examines them to determine the destination window, and then posts them to the message queue of the thread that created the destination window.

e. A thread's message queue receives all mouse and keyboard messages for the windows created by the thread. The thread removes messages from its queue and directs the system to send them to the appropriate window procedure for processing.

2. Queued Messages

Nonqueued messages are sent immediately to the destination window procedure, bypassing the system message queue and thread message queue.

Handling of messages

1. A single-threaded application usually uses a message loop in its WinMain function to remove and send messages to the appropriate window procedures for processing.

2. Applications with multiple threads can include a message loop in each thread that creates a window.

3. Only one message loop is needed for a message queue, even if an application contains many windows.

Window Procedure

1. A window procedure is a function that receives and processes all messages sent to the window. Every window class has a window procedure, and every window created with that class uses that same window procedure to respond to messages.

2. Each window has a function, called a window procedure, that the system calls whenever it has input for the window. The window procedure processes the input and returns control to the system.

The sample "Hello World" program would help understand a few concepts



HINSTANCE hinst;
HWND hwndMain;

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
BOOL bRet;
WNDCLASS wc;
UNREFERENCED_PARAMETER(lpszCmdLine);

// Register the window class for the main window.

if (!hPrevInstance)
{
wc.style = 0;
wc.lpfnWndProc = (WNDPROC) MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon((HINSTANCE) NULL,
IDI_APPLICATION);
wc.hCursor = LoadCursor((HINSTANCE) NULL,
IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "MainMenu";
wc.lpszClassName = "MainWndClass";

if (!RegisterClass(&wc))
return FALSE;
}

hinst = hInstance; // save instance handle

// Create the main window.

hwndMain = CreateWindow("MainWndClass", "Sample",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, (HWND) NULL,
(HMENU) NULL, hinst, (LPVOID) NULL);

// If the main window cannot be created, terminate
// the application.

if (!hwndMain)
return FALSE;

// Show the window and paint its contents.

ShowWindow(hwndMain, nCmdShow);
UpdateWindow(hwndMain);

// Start the message loop.

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

// Return the exit code to the system.

return msg.wParam;
}

LRESULT CALLBACK MainWndProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter
{

switch (uMsg)
{
case WM_CREATE:
// Initialize the window.
return 0;

case WM_PAINT:
// Paint the window's client area.
return 0;

case WM_SIZE:
// Set the size and position of the window.
return 0;

case WM_DESTROY:
// Clean up window-specific data objects.
return 0;

//
// Process other messages.
//

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

Sunday, September 14, 2008

Threads - UI and Worker threads

Lets start with the basics. We would first try to gain an understanding of native threads and then gradually move to managed threads.

A thread is the basic unit to which the operating system allocates processor time. Each process is started with a single thread, often called the primary thread, but can create additional threads from any of its threads.

The figure below shows variety of models for threads and processes.

1. MS-DOS supports a single process and single thread.



Now, there can be a slight difference between the GUI application and console applications. There were two kinds of threads

1. User-Interface Threads (GUI Threads) : Commonly used to handle user input and respond to user events.

2. Worker Threads : Commonly used to handle background tasks that the user shouldn't have to wait for to continue using your application.

But why is there a distinction between user-interface threads and worker threads ?

1. User Interface thread has its own message pump/loop to process the messages in its message queue. It can implement message maps and message handlers. Worker thread does not have its own message pump. Any thread can create a window. The thread that creates the window becomes a GUI thread, it owns the window and its associated message queue.

2. A worker thread often terminates once its work is done. On other hand a UI-Thread often remains in the memory standby (inside Run() method, which does message pumping) to do the work on receiving any message.

Multiple Threads and GDI Objects

To enhance performance, access to graphics device interface (GDI) objects (such as palettes, device contexts, regions, and the like) is not serialized. This creates a potential danger for processes that have multiple threads sharing these objects. For example, if one thread deletes a GDI object while another thread is using it, the results are unpredictable. This danger can be avoided simply by not sharing GDI objects. If sharing is unavoidable (or desirable), the application must provide its own mechanisms for synchronizing access.

In Win32 programming, CreateThread function is used to create a thread. It takes starting address of the code the new thread is to execute. If the thread is a GUI thread, it is its responsibility to provide a message loop.

In MFC, you were supposed to derive from CWinThread to create a user-interface thread.

Friday, September 12, 2008

Exceptions and Multiple Event Handlers

If you have multiple handlers for an event, when the event is raised, each of the handlers gets called in turn by the .NET Framework. So far, so good. What happens if one of the event handlers raises an exception? Then things don't go so well.

If any event listener raises an exception, the whole event-handling chain stops. If you pause to consider what's going on, this behavior makes sense. If an exception occurs in any listener, the exception bubbles back to the event-raising code, and the .NET Framework calls no more of the event listeners, and event handling grinds to a halt.

There is an alternative. Rather than simply raising the event, it is possible for you to call each individual listener explicitly. You can take advantage of the members of the Delegate class to solve the problem of unhandled exceptions in multiple listeners.



class Program {
static void Main(string[] args) {

BankAccount b = new BankAccount();
b.LargeAmountEvent += new LargeAmountEventHandler(b_LargeAmountEvent1);
b.LargeAmountEvent += new LargeAmountEventHandler(b_LargeAmountEvent2);
b.Withdraw(100);
}

// Throws an exception
static private void b_LargeAmountEvent1(Object sender, BankEventArgs e) {

if (true) {
throw new Exception("exception");
}
Console.WriteLine("Withdrawn" + e.Amount);
}


// Handler which does not throw an exception
static private void b_LargeAmountEvent2(Object sender, BankEventArgs e) {

Console.WriteLine("Withdrawn" + e.Amount);

}
}/*End Program*/


public class BankEventArgs : EventArgs {
private int amount;

public BankEventArgs(int amount) {
this.amount = amount;
}

public int Amount {
get { return amount; }
}
}



public delegate void LargeAmountEventHandler(Object sender, BankEventArgs e);



public class BankAccount {

public event LargeAmountEventHandler LargeAmountEvent;

public void Withdraw(int amount){
OnLargeWithdraw(new BankEventArgs(amount));
}

protected void OnLargeWithdraw(BankEventArgs e){

if (LargeAmountEvent != null) {
// Instead of raising event, call GetInvocationList
Delegate[] d = LargeAmountEvent.GetInvocationList();
foreach (LargeAmountEventHandler evnt in d) {
try {
evnt.Invoke(this, e);
} catch (Exception ex) {
Console.WriteLine(ex.Message);
}
}
//LargeAmountEvent(this, e);
}
}
}

Sunday, September 7, 2008

Updating the UI from a Secondary Thread

A very nice article describing how to switch from secondary thread to primary thread.

i. Control.Invoke
ii. Control.BeginInvoke
iii. Control.InvokeRequired

Asynchronous Method Execution Using Delegates

There are a number of scenarios in which asynchronous execution can be a valuable design technique. Delegates provide an easy, powerful abstraction for asynchronous method execution.

Let's assume that we have the method GetCustomerList which is a long running method and we want to execute it asynchronously.



class DataAccessCode{
public static string[] GetCustomerList(string state){
// call across network to DBMS or Web Services to retrieve data
// pass data back to caller using string array return value
}
}



This method can be executed asynchronously using delegates.

1. Define a delegate type matching the signature of the method you want to invoke asynchronously.

public delegate string[] LongRunning(string state);

2. Create a delegate object and bind it to the GetCustomerList method.

LongRunning method = new LongRunning(DataAccessCode.GetCustomerList);

3. Call BeginInvoke.

a. When you make a call to BeginInvoke on a delegate object, you are essentially asking the common language runtime (CLR) to begin executing the handler method asynchronously on a secondary thread. While executing a method asynchronously with a call to BeginInvoke is powerful, it's also fairly easy because you don't have to be concerned with creating and managing a secondary thread. The CLR does this for you automatically.

b. When you call the BeginInvoke method, the delegate object places a request in a special internal queue. The CLR maintains a pool of worker threads that are responsible for servicing the request in this queue. Asynchronous execution is achieved because the thread that calls BeginInvoke is not the same as the thread that executes the handler method.

c. The call to BeginInvoke returns right away and the calling thread can then move on to other business without having to wait for the CLR to execute the target method.

IAsyncResult result = method.BeginInvoke("Delhi",null, null);

4. Call EndInvoke

string[] customers = method.EndInvoke(result);

a. EndInvoke allows you to retrieve the return value from an asynchronous method call.

b. A call to EndInvoke requires you to pass the IAsyncResult object associated with a particular asynchronous call.

c. Parameter list for EndInvoke will include any ref parameters defined within the delegate type so you can also retrieve any output parameters.

d. If the asynchronous method experiences an unhandled exception, that exception object is tracked by the CLR and then thrown when you call EndInvoke.

e. Forgetting to call EndInvoke will prevent the CLR from cleaning up some of the resources required in dispatching asynchronous calls. Therefore, you should assume your application will leak if you make calls to BeginInvoke without also making associated calls to EndInvoke.

f. A call to EndInvoke returns right away if the worker thread has already completed the execution of the handler method. However, a call to EndInvoke will block if the asynchronous call hasn't yet started or is still in progress.

g. When to call EndInvoke

i. Immediately after calling BeginInvoke

ii. Wait on WaitHandle and then call EndInvoke

iii. Poll for IsCompleted to be true and then call EndInvoke when it is true.

iv. From callback

In a GUI application, all the code usually runs on a single thread known as the primary UI thread. The primary UI thread is important in a Windows Forms UI application because it is in charge of maintaining the responsiveness of the user interface. If you freeze the primary user interface thread with a long-running call across the network, the hosting application will be unresponsive to the user until the call returns. So, you should take advantage of a handy feature of delegates that lets you set up a callback method that's automatically executed by the CLR when an asynchronous method call is completed.

When the application calls BeginInvoke, the CLR executes the handler method using a worker thread from the CLR thread pool. Next, that same worker thread executes the callback method. When this work is complete, the CLR returns that worker thread to the pool so it is available to service another asynchronous request.

It's very important to understand that the callback method does not run on the primary UI thread that made the call to BeginInvoke. Once again, the callback method is executed on the same secondary thread that executed the asynchronous method.

There is an important threading rule to follow when programming with Windows Forms. The primary UI thread is the only thread that's allowed to touch a form object and all of its child controls. That means it's illegal for code running on a secondary thread to access the methods and properties of a form or a control. However, you must remember that a callback method executes on a secondary thread and not the primary UI thread. That means you should never attempt to update the user interface directly from this kind of callback method. To be more concise, the callback method that's running on the secondary thread must force the primary UI thread to execute the code that updates the user interface.






IAsyncResult

public interface IAsyncResult {
object AsyncState { get; }
WaitHandle AsyncWaitHandle { get; }
bool CompletedSynchronously { get; }
bool IsCompleted { get; }
}

a. The IAsyncResult object allows you to monitor an asynchronous call in progress.
b. The property IsComplete allows you to monitor the status of an asynchronous call and determine whether it has completed.

AsyncCallback

public delegate void AsyncCallback(IAsyncResult ar);

Saturday, September 6, 2008

Demystifying Delegates

1. public delegate Int32 Add(Int32 a, Int32 b);

When compiler sees this line, it actually defines a class


public class Add: System.MulticastDelegate {

// Constructor
public Add(Object target, Int32 methodPtr);

// Method with same prototype as specified by the source code
public void virtual Invoke(Int32 a, Int32 b){
// If there are any delegates in the chain that
// should be called first, call them
if (_prev != null) _prev.Invoke(a, b);

// Call our callback method on the specified target object
_target.methodPtr(a, b);

}

// Methods allowing the callback to be called asynchronously
public virtual IAsyncResult BeginInvoke(Int32 a, Int32 b,
AsyncCallback callback, Object object);
public virtual void EndInvoke(IAsyncResult result);
}


2. All delegates are derived from MulticastDelegate.

3. The signature of the Invoke method matches the signature of delegate exactly.

4. In the code below, the constructor Add is taking one parameter but the generated class's constructor has two parameters. Well, compiler magic again

a. The compiler knows that a delegate is being constructed, and the compiler parses the source code to determine which object and method are being referred to. A reference to the object is passed for the target parameter, and a special Int32 value (obtained from a MethodDef or MethodRef metadata token) that identifies the method is passed for the methodPtr parameter.
b. For static methods, null is passed for the target parameter. Inside the constructor, these two parameters are saved in their corresponding private fields.




class Test{

Int32 MyAdd(Int32 a, Int32 b);
static Int32 StMyAdd(Int32 a, Int32 b);

}

Test obj = new Test();
Add a = new Add(obj.MyAdd)



5. There are three private fields that one should be aware of
private IntPtr _methodPtr;
private object _target;
private MulticastDelegate _prev;

6. Adding more than one callback methods
Add obj = null;
obj += new Add(Test.StMyAdd);
obj += new Add(Test.StMyAdd);

You can also use the static Combine methods of the Delegate class.

7. The field _prev allows delegate objects to be part of a linked-list.

8. http://msdn.microsoft.com/en-us/magazine/cc301810.aspx
http://msdn.microsoft.com/en-us/magazine/cc301816.aspx

Uses of delegates

1. Used for implementing callbacks.

2. Used for implementing multicasting.

3. Delegates also provide the primary means for executing a method on a secondary thread in an asynchronous fashion.

Things you should know about events

The recommended design pattern that should be used to expose events.

1. Define a type which will hold any additional information that should be sent to receivers of the event notification. By convention, types that hold event information are derived from System.EventArgs and the name of the type should end with "EventArgs".

The EventArgs type is inherited from Object and looks like this:

[Serializable]
public class EventArgs {
public static readonly EventArgs Empty = new EventArgs();
public EventArgs() { }
}

It simply serves as a base type from which other types may derive. Many events don't have any additional information to pass on. When you are defining an event that doesn't have any additional data to pass on, just use EventArgs.Empty.

2. Define a delegate type specifying the prototype of the method that will be called when the event fires. By convention, the name of the delegate should end with "EventHandler". It is also by convention that the prototype have a void return value and take two parameters. The first parameter is an Object that refers to the object sending the notification, and the second parameter is an EventArgs-derived type containing any additional information that receivers of the notification require.

If you're defining an event that has no additional information that you want to pass to receivers of the event, then you do not have to define a new delegate.

You can use the System.EventHandler delegate and pass EventArgs.Empty for the second parameter. The prototype of EventHandler is as follows:

public delegate void EventHandler(Object sender, EventArgs e);

3. Define an event.

4. Define a protected, virtual method responsible for notifying registered objects of the event. The OnXXX method is called when a event occurs. This method receives an initialized XXXEventArgs object containing additional information about the event. This method should first check to see if any objects have registered interest in the event and if so, the event should be fired.

This gives the derived type control over the firing of the event. The derived type can handle the event in any way it sees fit. Usually, a derived type will call the base type's OnXXX method so that the registered object will receive the notification. However, the derived type may decide not to have the event forwarded on.

5. Finally, define a method that translates the input into the desired event.

It is also useful to know what compiler does when it sees the event keyword. This also would help us understand why a new type "event" was required. For example, say we have the following line

public event EventHandler MailMsg;

The C# compiler translates this single line of source code into three constructs

1. The first construct is simply a field that is defined in the type. This field is a reference to the head of a linked list of delegates that want to be notified of this event. This field is initialized to null, meaning that no listeners have registered interest in the event.

You'll notice that event fields (MailMsg in this example) are always private even though the original line of source code defines the event as public. The reason for making the field private is to prevent code outside the defining type from manipulating the field improperly.

// A PRIVATE delegate field that is initialized to null
private EventHandler MailMsg = null;

2. The second construct generated by the C# compiler is a method that allows other objects to register their interest in the event. The C# compiler automatically names this function by prepending "add_" to the field's name (MailMsg).

// A PUBLIC add_* method.
// Allows objects to register interest in the event


MethodImplAttribute(MethodImplOptions.Synchronized)]
public void add_MailMsg(EventHandler handler) {
MailMsg = (EventHandler)
Delegate.Combine(MailMsg, handler);
}


3. The C# compiler is a method that allows an object to unregister its interest in the event. Again, the C# compiler automatically names this function by prepending "remove_" to the field's name (MailMsg).

// A PUBLIC remove_* method.
// Allows objects to unregister interest in the event
[MethodImplAttribute(MethodImplOptions.Synchronized)]
public void remove_Click (MailMsgEventHandler handler) {
MailMsg = (MailMsgEventHandler)
Delegate.Remove(MailMsg, handler);
}


Other facts that you should know about the events.

1. The thread used when firing the event, is the thread used to handle the event.

2. When an event has multiple subscribers, the event handlers are invoked synchronously when an event is raised.

3. It is necessary to unsubscribe from events to prevent resource leaks. The publisher holds a reference of the subscriber and the garbage collector would not delete the subscriber object until you unsubscribe from the event. So, you should unsubscribe from events before you dispose of a subscriber object.