第17章 委托
17.1 初识委托
using System;
namespace EnumApp
{
static class Program
{
static void Main()
{
MyDelegate[] ds = { new MyDelegate(Print1), new MyDelegate(Print2) };
int[] a = { 100, 1000 };
Execute(a, ds, a.Length);
}
public static void Print1(int i)
{
Console.WriteLine(i);
Console.WriteLine("Print1");
Console.WriteLine();
}
public static void Print2(int i)
{
Console.WriteLine(i);
Console.WriteLine("Print2");
Console.WriteLine();
}
public static void Execute(int[] a, MyDelegate[] ds, int length)
{
for (int i = 0; i < length; i++)
ds[i](a[i]);
}
}
public delegate void MyDelegate(int i);
}
17.2 用委托回调静态方法
将一个方法绑定到委托时,C#和CLR都允许引用类型的协变性(covariance)和逆变性(contravariance)。协变性是指方法能返回从委托的返回类型派生的一个类型。逆变性是指是方法获取的参数可以是委托的参数类型的基类。
协变性和逆变性只能应用于引用类型,不能用于值类型和void。
17.4 委托揭秘
由于 委托是类,所以凡是能够定义类的地方,都能定义委托。
由于所有委托类型都派生自MulticastDelegate,所以他们继承了MulticastDelegate的字段属性和方法。
Delegate类定义了两个只读的公共实例属性:Target和Method。
Target属性返回一个引用,它指向回调方法要操作的对象。Method属性返回对于个System.Reflection.MethodInfo对象的引用,该对象标识了回调方法。
可以调用Invoke方法来执行回调方法。
public static void Execute(int[] a, MyDelegate[] ds, int length)
{
for (int i = 0; i < length; i++)
{
Console.WriteLine(ds[i].Target == null ? "null" : ds[i].Target); //null
Console.WriteLine(ds[i].Method); //Print#(int32)
ds[i].Invoke(a[i]);//ds[i](a[i]);
}
}
17.5 用委托回调许多方法(委托链)
使用 Delegate类的公共静态方法Combine,可以将一个委托添加到链中。
使用Delegate的公共静态方法Delete,可以从链中删除委托。如果链中删除了仅有的一个元素,Remove会返回null。
result变量将只包含调用的最后一个委托的结果,前面的返回值会被丢弃。
+=,-+这些操作符分别调用Delegate.Combine和Delegate.Remove。
MulticastDelegate类提供了一个实例方法GetInvocationList,用于显式调用链中的每一个委托,同时使用符合自己需要的任何算法。
using System;
namespace EnumApp
{
static class Program
{
static void Main()
{
Class1 c1 = new Class1();
MyDelegate d1 = new MyDelegate(c1.Method1);
MyDelegate d2 = new MyDelegate(c1.Method2);
MyDelegate d3 = new MyDelegate(c1.Method3);
MyDelegate d0 = null;
d0 = (MyDelegate)Delegate.Combine(d0, d1);
d0 = (MyDelegate)Delegate.Combine(d0, d2);
d0 = (MyDelegate)Delegate.Combine(d0, d3);
Console.WriteLine(d0.Invoke(2)); //最后加载的委托的执行result 0
foreach (var d in d0.GetInvocationList())
d.DynamicInvoke(3);
}
}
public delegate int MyDelegate(int i);
public class Class1
{
public int Method1(int i)
{
Console.WriteLine("i*i={0}", i * i);
return i * i;
}
public int Method2(int i)
{
Console.WriteLine("i+i={0}", i + i);
return i + i;
}
public int Method3(int i)
{
Console.WriteLine("i-i={0}", i - i);
return i - i;
}
}
}
17.6 委托定义太多(泛型委托)
FCL提供了17个Action委托,它们从无参数一直到最多16个参数。
除了Action委托,.Net Framework还提供了17个Func函数,它们允许回调方法返回一个值。
using System;
namespace EnumApp
{
static class Program
{
static void Main()
{
Class1 c1 = new Class1();
Func<int, int> d1 = new Func<int, int>(c1.Method1);
Console.WriteLine(d1.Invoke(2));
}
}
public class Class1
{
public int Method1(int i)
{
return i * i;
}
}
}
17.7 C#为委托提供的简化语法
lambda表达式可在编译器预计会看到一个委托的地方使用。编译器看到这个lambda表达式后,会在类中自动定义一个新的私有方法。这个新方法称为匿名函数,因为方法的名字是编译器自动创建的。
Class1 c1 = new Class1();
Func<int, int> d1 = new Func<int, int>(p => { Console.WriteLine(p * p); return p * p; });
d1.Invoke(3);
17.8 委托和反射
所有CreateDelegate方法构造的都是从Delegate派生的一个类型的新对象,具体类型由第一个参数type来标识。MethodInfo参数指出应该回调的方法。
using System; using System.Reflection; namespace EnumApp { internal delegate int MyDelegate(int i); static class Program { static void Main() { Class1 c1 = new Class1(); Delegate d = Delegate.CreateDelegate(typeof(MyDelegate), new Class1(), typeof(Class1).GetMethod("Method1", BindingFlags.Public | BindingFlags.Instance), false); Console.WriteLine(d.DynamicInvoke(100)); } } public class Class1 { public int Method1(int i) { return i * i; } } }