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 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 intDel = sum; Console.WriteLine(intDel(3)); // Construct a string delegate. Also use method group conversion. SomeOp 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 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( ):
SomeOpintDel = 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( ):
SomeOpstrDel = 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:
SomeOpintDel = reflect; // Error!
Because reflect( ) takes a string argument and returns a string result, it cannot be assigned to an int version of SomeOp.
// 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 source, V args); // Declare an event class. class MyEvent { static int count = 0; public event MyEventHandler 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 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 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
No comments:
Post a Comment