委托
委托是一个对象,它知道如何调用一个方法。
委托类型 & 委托实例
委托类型定义了委托实例可以调用的那类方法,具体来说,委托类型定义了方法的返回类型和参数。
1 | //Transformer这个委托,所需参数和返回类型均为 int, 与下面的方法 Square() 一致,所以该委托可调用这个方法。 |
委托实例
- 委托的实例 其实就是调用者的委托:调用者调用委托,然后委托调用目标方法。
- 间接的把调用者和目标方法解耦了。
Demo1:
把方法赋值给委托变量的时候就创建了委托实例。
1
2Transformer t = Square; //简写形式
Transformer t = new Transformer(Square); //完整形式调用
1
2int answer = t(3); //简写形式,answer is 9
t.invoke(3); //完整形式
编写插件式的方法
- 方法是在运行时才赋值给委托变量
Demo2:
多播委托
所有的委托实例都具有多播的能力。一个委托实例可以引用一组目标方法。
- “+” 和 “ += ” 操作符可以合并委托实例
1
2
3
4SomeDelegate d = SomeMethod 1;
d += SomeMethod2;
d = d + SomeMethod2;
- 调用d就会调用SomeMethod1 和 SomeMethod2
- 委托的调用顺序与它们的定义顺序一致
“ - ” 和 “ -= ” 会把右边的委托从左边的委托里 移除
1
d -= SomeMethod1;
委托变量使用 + 或 += 时,其操作数可以是 null。
- 使用 += 或 -= 时,实际上是创建了新的委托实例,并把它赋给当前的委托变量。
- 如果多播委托的返回类型不是 void,那么调用者从最后一个被调用的方法来接受返回值。前面的方法仍会被调用,但是其返回值被弃用了。
实例方法目标 和 静态方法目标
- 当一个实例方法被赋值给委托对象的时候,这个委托对象不仅要保留着对方法的引用,还要保留着方法所属实例的引用。
- System.Delegate 的 Target属性 就代表着这个实例。
如果引用的是静态方法,那么Target属性的值就是 null。
Demo3:
泛型委托类型
- 泛型委托类型可以包含泛型类型参数
1
public delegate T Transformer<T> (T arg);
Demo:
Func 和 Action 委托 (略过)
使用泛型委托,就可以写出这样一组委托类型,它们可调用的方法可以拥有任意的返回类型和任意(合理)数量的参数
可以把上面Demo中的 Transform方法中的委托参数改为:结果也是一样的。1
public static void Transform<T>(T[] values, Func<T, T> t)
委托 VS 接口
委托可以解决的问题,接口都可以解决。
什么情况下更适合使用委托而不是接口呢?当下列条件满足之一:
- 接口只能定义一个方法
- 需要多播能力
- 订阅者需要多次实现接口
委托的兼容性 - 委托类型
委托类型之间互不相容,即使 签名方法一样:
1
2
3
4
5delegate void D1();
delegate void D2();
...
D1 d1 = Method1;
D2 d2 = d1; //Compile - time error如果委托实例拥有相同的方法目标,那么委托实例就认为是相等的。
1
2
3
4
5delegate void D();
...
D d1 = Method1;
D d2 = Method1;
console.WriteLine( d1 == d2); //True
委托的兼容性 - 参数
- 当调用一个方法时,提供的参数(argument)可以比方法的参数(parameter) 定义更具体。
- 委托可以接受比它的方法目标更具体的参数类型,这叫 ContraVariance. 逆变
委托的兼容性 - 返回类型
- 调用方法时,可以得到一个比请求的类型更具体的类型的返回结果
- 委托的目标方法可以返回bi委托描述里更具体的类型返回结果。Co’v’ariance 协变