Function pointers are indirect references to functions and support calling functions through variables. With function pointers, you can pass a function as a parameter to a function or as a return type. Function pointers are an important ingredient of many programming languages, including C, C++, and Microsoft Visual Basic. Function pointers can make applications more flexible, extensible, and scalable. Function pointers have been the source of many application bugs, however, because function pointers point to raw bytes of memory. Function pointers are not type-safe, and calling an incompatible method through a function pointer is an infrequent error. .NET has delegates that are essentially type-safe function pointers. Unlike function pointers in other languages, particularly C and C++, delegates are type-safe, object-oriented, and secure. This reduces common problems associated with using function pointers.
A delegate is an abstraction of one or more function pointers. Delegates are derived from System.MulticastDelegate, which is a reference type. System.MulticastDelegate is derived from System.Delegate. The delegate classes offer a public interface for initializing, adding, removing, and invoking delegates. An instance of a delegate is an object that abstracts the semantics of a function pointer. The object encapsulates a function pointer and a target object. When a delegate is invoked, the delegate calls the function on that object.
Delegates have a signature and a return type. A function pointer dropped into the delegate must have a compatible signature, which makes the function pointer type-safe. The return type should also match. Delegate covariance, which is discussed later in the chapter, provides some flexibility with the signature. You assign function pointers to delegates based on signature, not type. Regardless of the object or type that binds the function, it is assignable to a delegate of the same signature.
Functions called with a delegate are given the security context of the caller, which prevents a delegate from performing a task not available to a lower-privilege caller. Delegates can be initialized with pointers to functions that are implemented anywhere. The only limitation is the signature. Callers need to be careful when invoking delegates containing function pointers to unknown sources, where there could be unexpected implementation. Use code access security to protect delegates.
Delegates are useful as general function pointers, callbacks, events, and threads. As a general function pointer, a delegate can be a method parameter, function return, class member, or local variable. Callbacks are valuable in many scenarios, including promoting peer-to-peer relationships, in which objects swap function pointers to send bilateral messages. Events support a publisher/subscriber model. The publisher notifies subscribers of events, whereas the subscriber registers functions to be called when the event occurs. Finally, a delegate is a path of execution for a thread, which is an asynchronous function call. This chapter focuses on general function pointers, callbacks, and events. Threads are discussed only in this context.
Many .NET applications are event-driven. Event-driven architecture is common in the .NET environment with Microsoft Windows Forms, ASP.NET, and XML Web Service applications. The lifetime of an event-driven application is spent waiting for events to handle, such as a paint event for Windows Forms, a page load event in an ASP.NET application, or a session event in an XML Web Service application. Although procedural applications execute linearly, event-driven code runs disjointedly. The sequence of code in an event-driven application is determined at run time from the user interface and other input sources. Poorly designed or implemented procedural code is commonly referred to as spaghetti code. Poorly implemented code for an event-driven application is called ravioli code and it can literally run in circles. The randomness of an event-driven application makes poorly written code harder to maintain and debug.
Events are the most practical application of delegates. Events mark an occurrence as defined by an application. For proper application design, events in an application should mirror events in the problem domain. Events represent a wide variety of actions and include predefined and custom events. Predefined events include button click, page load, timer, unhandled exception, and so on. Custom events include events for monitoring hardware devices, starting and stopping communications, overdrawing a checking account, setting alarms, and so on.
The event model includes subscribers and publishers: A publisher is an object or type that exposes an event; a subscriber registers for an event with a delegate. The delegate contains the response to the event. When an event is raised, publishers invoke the delegates of any subscribers. The Observer pattern, which includes the event patterns, documents the best practices of an event-driven environment. Use the following link to view the Observer pattern on the Microsoft Developer Network (MSDN): http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/observerpattern.asp.