So far in this chapter, the use of delegates has required the developer to know up front the prototype of the method that is to be called back. For example, if fb is a variable that references a Feedback delegate (see this chapter’s first program listing), to invoke the delegate, the code would look like the following.
fb(item); // item is defined as Int32
As you can see, the developer must know when coding how many parameters the callback method requires and the types of those parameters. Fortunately, the developer almost always has this infor- mation, so writing code like the preceding code isn’t a problem.
In some rare circumstances, however, the developer doesn’t have this information at compile time.
I showed an example of this in Chapter 11, “Events,” when I discussed the EventSet type. In this example, a dictionary maintained a set of different delegate types. At run time, to raise an event, one of the delegates was looked up in the dictionary and invoked. At compile time, it wasn’t possible to know exactly which delegate would be called and which parameters were necessary to pass to the delegate’s callback method.
Fortunately, System.Reflection.MethodInfo offers a CreateDelegate method that allows you to create a delegate when you just don’t have all the necessary information about the delegate at compile time. Here are the method overloads that MethodInfo defines.
public abstract class MethodInfo : MethodBase {
// Construct a delegate wrapping a static method.
public virtual Delegate CreateDelegate(Type delegateType);
// Construct a delegate wrapping an instance method; target refers to the ‘this’ argument. public virtual Delegate CreateDelegate(Type delegateType, Object target);
}
After you’ve created the delegate, you can call it by using Delegate’s DynamicInvoke method, which looks like the following.
public abstract class Delegate {
// Invoke a delegate passing it parameters
public Object DynamicInvoke(params Object[] args);
}
Using reflection APIs (discussed in Chapter 23, “Assembly Loading and Reflection”), you must first acquire a MethodInfo object referring to the method you want to create a delegate to. Then, you call the CreateDelegate method to have it construct a new object of a Delegate-derived type identi- fied by the first parameter, delegateType. If the delegate wraps an instance method, you will also pass to CreateDelegate a target parameter indicating the object that should be passed as the this parameter to the instance method.
System.Delegate’s DynamicInvoke method allows you to invoke a delegate object’s callback method, passing a set of parameters that you determine at run time. When you call DynamicInvoke, it internally ensures that the parameters you pass are compatible with the parameters the callback method expects. If they’re compatible, the callback method is called. If they’re not, an Argument Exception is thrown. DynamicInvoke returns the object the callback method returned.
The following code shows how to use the CreateDelegate and DynamicInvoke methods.
using System;
using System.Reflection; using System.IO;
// Here are some different delegate definitions internal delegate Object TwoInt32s(Int32 n1, Int32 n2); internal delegate Object OneString(String s1);
public static class DelegateReflection { public static void Main(String[] args) {
if (args.Length < 2) { String usage =
@"Usage:" +
"{0} delType methodName [Arg1] [Arg2]" +
"{0} where delType must be TwoInt32s or OneString" +
"{0} if delType is TwoInt32s, methodName must be Add or Subtract" + "{0} if delType is OneString, methodName must be NumChars or Reverse" + "{0}" +