.NET via C#笔记17——委托

一、委托的内部实现

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方法可以删除委托(链)中从后向前找到的第一个符合条件的委托

  1. 如果没有了,返回null
  2. 如果只剩一个委托,返回这个委托。
  3. 如果还有多余一个委托,创建一个新的委托,和对应的数组,把剩下的委托拷贝进去。

带返回值的委托

如果委托类型带有返回值,对一个委托链进行Invoke操作会返回委托链中最后一个委托的调用结果。

显式控制委托链的调用

Delegate[] MultiCastDelegate.GetInvocationList()

三、语法糖和lambda表达式

  1. 可以用+=-=表示Delegate.CombineDelegate.Remove方法。
  2. 隐式地使用静态函数或成员函数新建委托实例。
    void foo() {}
    void bar() {
        Action action = foo;
        action += foo;
        action -= foo;
    }
    

lambda表达式

int a = 0;
int b = 1;
Action action = ()=> { Console.WriteLine(a + b); };

分两种情况:

  1. 没有引用局部变量,lambda表达式会转化为类中的一个私有方法。
    1. 如果没有引用类的成员函数或成员变量,这是一个静态方法。
    2. 否则会成为一个成员方法。
  2. 如果lambda表达式引用了局部变量,编译器会为lambda表达式创建一个局部类。
    1. 类的成员变量用来存储引用的局部变量。
    2. 类的成员函数表示这个lambda表达式。
posted @ 2020-02-26 21:34  马子哥  阅读(182)  评论(0编辑  收藏  举报