一、委托的内部实现
C#中的委托是一种类型安全的回调函数,假设有这样一个委托:
internal delegate void Feedback(int value);
编译器会生成一个类:
internal class Feedback : MultiCastDelegate {
public Feedback(object @object, IntPtr method);
// 用于同步回调
public virtual void Invoke(int value);
// 用于异步回调
public virtual IAsyncResult BeginInvoke(int value, AsyncCallback callback, object @object);
public virtual void EndInvoke(IAsyncResult result);
}
当创建一个委托时,编译器会把静态函数,成员函数等转化为这个类的实例对象。
注意这里的修饰符是internal
和deletage的修饰符是一致的。
二、委托链
MultiCastDelegate
再来看下MultiCastDelegate
这个基类,它有三个重要字段:
字段 | 类型 | 说明 |
---|---|---|
_target |
object |
如果引用一个实例方法,这里会存this |
_methodPtr |
IntPtr |
标示回调的方法 |
_invocationList |
object |
用于表示委托数组 |
这个类又继承自Delegate ,这是C#的历史遗留问题,有些地方还需要用到Delegate 作为参数类型。 |
Delegate.Combine
使用void Delegate.Combine(Delegate a, Delegate b)
方法可以创建一个委托链。如果有一个参数为null
返回值为非null
的那个参数;如果都不是null
,返回值为一个新的委托,其_invocaionList
指向一个数组,数组的元素为这两个参数。
a |
b |
返回值 |
---|---|---|
null |
delegate1 | delegate1 |
delegate2 | null |
delegate2 |
delegate1 | delegate2 | delegateChain |
Delegate.Remove
通过Remove方法可以删除委托(链)中从后向前找到的第一个符合条件的委托
- 如果没有了,返回
null
。 - 如果只剩一个委托,返回这个委托。
- 如果还有多余一个委托,创建一个新的委托,和对应的数组,把剩下的委托拷贝进去。
带返回值的委托
如果委托类型带有返回值,对一个委托链进行Invoke操作会返回委托链中最后一个委托的调用结果。
显式控制委托链的调用
Delegate[] MultiCastDelegate.GetInvocationList()
三、语法糖和lambda表达式
- 可以用
+=
和-=
表示Delegate.Combine
和Delegate.Remove
方法。 - 隐式地使用静态函数或成员函数新建委托实例。
void foo() {} void bar() { Action action = foo; action += foo; action -= foo; }
lambda表达式
int a = 0;
int b = 1;
Action action = ()=> { Console.WriteLine(a + b); };
分两种情况:
- 没有引用局部变量,lambda表达式会转化为类中的一个私有方法。
- 如果没有引用类的成员函数或成员变量,这是一个静态方法。
- 否则会成为一个成员方法。
- 如果lambda表达式引用了局部变量,编译器会为lambda表达式创建一个局部类。
- 类的成员变量用来存储引用的局部变量。
- 类的成员函数表示这个lambda表达式。