一, 当我们使用关键字 delegate 声明一个自定义委托类型时, 实际上是声明了一个该名称的类类型, 继承自抽象类 System.MulticastDelegate, 还包含实例方法 Invoke,BeginInvoke,EndInvoke:
public delegate void MyDelegate();
其中的构造函数中第二个参数是 native int 类型的, 这个是什么呢? 我们接着看:
我们知道在 C# 中任何方法都可以直接赋值给签名一致的委托实例, 这个过程看似并不合理, 按理来说 C# 中不支持直接获取函数的指针, 其实这里是由编译器进行了取址操作, 查看 IL 代码可知:
MyDelegate myDelegate = myObj.MyFunc;
可以看到由编译器为我们进行了构建委托实例的过程, 而且这里调用了 ldftn 命令将实例方法 MyFunc()的 native int 类型的非托管指针推到栈中, 从而将该方法的指针传到委托的构造函数中;
由于上面的构造函数存在 C# 中不支持的函数指针类型 void(), 所以不能在运行时使用 Activator 类中的方法创建委托实例, 但在委托基类 Delegate 中存在静态方法 CreateDelegate()调用非托管代码用于动态创建委托实例, 命名空间 System.Reflection 中的方法信息类 MethodInfo 的实例方法 CreateDelegate()也提供了类似的方式以在运行时动态构建委托实例:
- Type delegateType = typeof(MyDelegate); // 这里以可访问到的委托类型举例
- Delegate @delegate = Delegate.CreateDelegate(delegateType, myObj, "MyFunc");
- //@delegate = typeof(MyClass).GetMethod("MyFunc").CreateDelegate(delegateType, myObj);
- // 添加其它委托实例
- @delegate = Delegate.Combine(@delegate, otherDelegate);
- // 调用委托
- @delegate.DynamicInvoke();
- // 当指定的委托类型可访问时, 可以将委托实例显式转换为指定的委托类型后使用 () 或 Invoke()正常调用
- //MyDelegate myDelegate = @delegate as MyDelegate;
- //myDelegate();
对委托实例或方法的 +,+= 操作实际上也是调用基类 Delegate 中的静态方法 Combine()并将合成后的委托强制转换为原类型后返回,-,-= 操作则是调用静态方法 Remove();
二, 委托的异步调用: 通过委托类型的实例方法 BeginInvoke 开启子线程并在该子线程中执行委托实例中的方法, 以此种方式调用的委托实例中有且只能有一个方法, 如果包含多个方法, 会抛出异常 ArgumentException:
myDelegate.BeginInvoke(null, null); // 其中第一个参数为 AsyncCallback 类型的回调函数
如果需要异步调用一个委托实例中方法列表中的所有方法, 需要先获取方法列表, 再依次进行异步调用:
- Delegate[] delegates = myDelegate.GetInvocationList();
- for (int i = 0; i <delegates.Length; i++)
- {
- (delegates[i] as MyDelegate).BeginInvoke(null, null);
- }
三, 当调用委托时, 如果方法列表中某个方法内引发异常且未在该方法体内捕获时, 该异常将传递给委托的调用方, 并且不再调用方法列表中的后面的方法, 因此在方法体内捕获异常显得尤为重要;
四, 泛型中的委托: 自定义泛型委托(Generic Delegate), 将类型参数用作参数列表或返回值的类型:
- delegate void MyDelegate<T>(T obj); // 声明具有一个类型参数的泛型委托, 参数列表中有一个参数
- void MyGenericFunc<T>(T obj) // 声明一个泛型方法, 参数列表中有一个参数
- {
- //do...
- }
- void MyFunc(string str)
- {
- //do...
- }
- // 声明泛型委托的实例, 指定类型参数为 string 类型, 此时可匹配的方法签名为 void myFunc(string str)
- MyDelegate<string> myDelegate;
- // 赋值一个指定类型参数为 string 的泛型方法
- myDelegate = MyGenericFunc<string>;
- // 添加一个参数列表为 string 类型的具体方法
- myDelegate += MyFunc;
※泛型委托同泛型类一样, 需要在实例化时指定类型参数的类型;
※泛型委托的实例同具体委托的实例一样, 只需要方法的参数列表和返回值类型相同即可进行匹配, 因此不管目标方法是指定了符合要求类型的泛型方法还是具体方法都可以进行匹配;
如果您觉得阅读本文对您有帮助, 请点一下 "推荐" 按钮, 您的认可是我写作的最大动力!
来源: https://www.cnblogs.com/minotauros/p/9806229.html