Generic Delegates


Like methods, delegates can also be generic. The syntax is similar to that of a generic method, with the type parameter being specified after the delegate’s name. To declare a generic delegate, use this general form:

 delegate ret-type delegate-name<type-parameter-list>(arg-list);

Notice the placement of the type parameter list. It immediately follows the delegate’s name. The advantage of generic delegates is that they let you define, in a type-safe manner, a generalized form that can then be matched to any specific type of method.

The following program demonstrates a generic delegate called SomeOp that has one type parameter called T. It returns type T and takes an argument of type T.

 // A simple generic delegate. using System; // Declare a generic delegate. delegate T SomeOp<T>(T v); class GenDelegateDemo {   // Return the summation of the argument.   static int sum(int v) {     int result = 0;     for(int i=v; i>0; i--)       result += i;     return result;   }   // Return a string containing the reverse of the argument.   static string reflect(string str) {     string result = "";     foreach(char ch in str)       result = ch + result;     return result;   }   public static void Main() {     // Construct an int delegate. Notice use of method group     // conversion on generic delegate.     SomeOp<int> intDel = sum;     Console.WriteLine(intDel(3));     // Construct a string delegate. Also use method group conversion.     SomeOp<string> strDel = reflect;     Console.WriteLine(strDel("Hello"));   } }

The output is shown here:

 6 olleH

Let’s look closely at this program. First, notice how the SomeOp delegate is declared:

 delegate T SomeOp<T>(T v);

Notice that T can be used as the return type even though the type parameter T is specified after the name SomeOp.

Inside GenDelegateDemo, the methods sum( ) and reflect( ) are declared, as shown here:

 static int sum(int v) { static string reflect(string str) {

The sum( ) method returns the summation of the integer value passed as an argument. The reflect( ) method returns a string that is the reverse of the string passed as an argument.

Inside Main( ), a delegate called intDel is instantiated and assigned a reference to sum( ):

 SomeOp<int> intDel = sum;

Because sum( ) takes an int argument and returns an int value, sum( ) is compatible with an int instance of SomeOp. Notice that the new C# 2.0 method group conversion syntax is used to assign sum to intDel. Method group conversions are fully compatible with generic delegates.

In similar fashion, the delegate strDel is created and assigned a reference to reflect( ):

 SomeOp<string> strDel = reflect;

Because reflect( ) takes a string argument and returns a string result, it is compatible with the string version of SomeOp.

Because of the type-safety inherent in generics, you cannot assign incompatible methods to delegates. For example, assuming the preceding program, the following statement would be in error:

 SomeOp<int> intDel = reflect; // Error!

Because reflect( ) takes a string argument and returns a string result, it cannot be assigned to an int version of SomeOp.

As explained in Chapter 15, one of the major uses of delegates occurs when handling events. Although events, themselves, cannot be generic, the delegate that supports an event can. The following program reworks an example from Chapter 15 (the .NET-compatible event demonstration) so that it uses a generic delegate:

 // Convert event example from Chapter 15 to // use generic delegate. using System; // Derive a class from EventArgs. class MyEventArgs : EventArgs {   public int eventnum; } // Declare a generic delegate for an event. delegate void MyEventHandler<T, V>(T source, V args); // Declare an event class. class MyEvent {   static int count = 0;   public event MyEventHandler<MyEvent, MyEventArgs> SomeEvent;   // This fires SomeEvent.   public void OnSomeEvent() {     MyEventArgs arg = new MyEventArgs();     if(SomeEvent != null) {       arg.eventnum = count++;       SomeEvent(this, arg);     }   } } class X {   public void handler<T, V>(T source, V arg) where V : MyEventArgs {     Console.WriteLine("Event " + arg.eventnum +                       " received by an X object.");     Console.WriteLine("Source is " + source);     Console.WriteLine();   } } class Y {   public void handler<T,V>(T source, V arg) where V : MyEventArgs {     Console.WriteLine("Event " + arg.eventnum +                       " received by a Y object.");     Console.WriteLine("Source is " + source);     Console.WriteLine();   } } class UseGenericEventDelegate {   public static void Main() {     X ob1 = new X();     Y ob2 = new Y();     MyEvent evt = new MyEvent();     // Add handler() to the event list.     evt.SomeEvent += ob1.handler;     evt.SomeEvent += ob2.handler;     // Fire the event.     evt.OnSomeEvent();     evt.OnSomeEvent();   } }

The output is shown here:

 Event 0 received by an X object. Source is MyEvent Event 0 received by a Y object. Source is MyEvent Event 1 received by an X object. Source is MyEvent Event 1 received by a Y object. Source is MyEvent




C# 2.0(c) The Complete Reference
C# 2.0: The Complete Reference (Complete Reference Series)
ISBN: 0072262095
EAN: 2147483647
Year: 2006
Pages: 300

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net