前言

这是个人对委托的理解系列第二篇,部分翻译自 Open Delegates vs. Closed Delegates – SLaks.Blog,好像还没人翻译过,加上部分个人理解。希望能对大家理解委托有所帮助。

正文

.Net支持两种委托:开放委托和封闭委托。open delegatesclosed delegates

译者注:这里不是作者这么分的,确实写在dotnet的官方文档和注释里。当然翻译的名称值得考量。

封闭委托:

当你创建一个指向实例对象方法的委托时,这个实例对象就被保存在委托的Target属性中,这个属性(也就是包含方法的实例对象)被当作方法的第一个参数传给委托指向的方法。对于实例对象的方法来说,就是this,对于静态方法来说,就是方法的第一个参数。这样的委托被称为封闭委托(closed delegates)因为他们以js闭包的方式把静态方法的第一个参数或者实例方法的this封进了委托实例之中。

译者注: 这里说的很抽象,意思就是封闭委托会把某些参数作为Target属性带进委托中。后面有例子说明。

开放委托

我们同样也能创建一个不隐含传第一个参数的开式委托,这种委托不使用Target属性,相反,所有委托目标方法的参数都是按委托的形参列表实际传递的,包括第一个参数。因此,一个指向给定方法的开放委托一定比指向同一个方法的封闭委托多传一个参数(也就是this或者第一个参数)。开放委托一般用于指向静态方法(同理:封闭委托一般用于实例方法)。当你创建一个指向静态方法的委托时,你一般不想这个委托持有这个方法的第一个参数。

译者注:原文比较抽象,结合后面的例子比较容易理解。

简短说明

除了上面说的这两种通用的情况来说(也就是静态方法创建开放委托,实例方法创建封闭委托),在.Net 2.0和之后我们还能创建指向实例方法的开放委托和指向静态方法的封闭委托。但是这样C#就没有相应的语法糖支持,只能通过Delegate.CreateDelegate方法来创建。

区分这两种委托主要看Target属性,原文的例子不太有些不太容易理解。这里结合自己的理解。重新写了例子,读完本文的例子可以再去看原文的例子就比较容易理解了。

一般情况的例子

一般情况的封闭委托(实例方法)


internal class HelloWorld
{
public void HelloWorldInstance() => Console.WriteLine("hello world"); public delegate void SayHi(); public void Main()
{
var helloWorldInstance = new SayHi(HelloWorldInstance);
}
}

这里实际调用的System.Private.CoreLib中的Delegate.CoreCLR.cs代码为:


[System.Diagnostics.DebuggerNonUserCode]
private void CtorClosed(object target, IntPtr methodPtr)
{
if (target == null)
ThrowNullThisInDelegateToInstance();
this._target = target;
this._methodPtr = methodPtr;
}

看名字就很容易理解,注意这里对Target的赋值且一定不为null

一般情况的开放委托(静态方法)


internal class HelloWorld
{
public static void HelloWorldStatic(string msg) => Console.WriteLine(msg); public delegate void SayHiWithParam(string msg); public void Main()
{
var helloWorld3 = new SayHiWithParam(HelloWorldStatic);
helloWorld3("hello world");
}
}

这里实际调用的System.Private.CoreLib中的MulticastDelegate.cs代码为:

[System.Diagnostics.DebuggerNonUserCode]
private void CtorOpened(object target, IntPtr methodPtr, IntPtr shuffleThunk)
{
this._target = this;
this._methodPtr = shuffleThunk;
this._methodPtrAux = methodPtr;
}

注意:

  • 这里的target实际为null,所以_target的赋值对象是this,而创建出的委托的Target属性为null
  • 这里_methodPtr的赋值对象是shuffleThunk

特殊情况的例子

前面说了,不一般情况下我们需要使用通过Delegate.CreateDelegate方法来创建委托。

特殊情况的封闭委托(静态方法)

    internal class HelloWorld
{
public static void HelloWorldStatic(string msg) => Console.WriteLine(msg); public void Main()
{
var closed = Delegate.CreateDelegate(typeof(Action), "a", typeof(HelloWorld).GetMethod(nameof(HelloWorld.HelloWorldStatic)));
closed.DynamicInvoke();
}
}

这里实际调用的System.Private.CoreLib中的Delegate.cs代码为:

// V2 api: Creates open or closed delegates to static or instance methods - relaxed signature checking allowed.
public static Delegate CreateDelegate(Type type, object? firstArgument, MethodInfo method) => CreateDelegate(type, firstArgument, method, throwOnBindFailure: true)!;

特别需要注意:

  • 这里创建的委托类型为:typeof(Action)不是typeof(Action<string>)因为,"a"做为第一个参数已经是委托的Target属性了。

特殊情况开放委托(实例方法)

    public class HelloWorld
{ public void HelloWorldInstance() => Console.WriteLine("hello world"); public void Main()
{
var open = Delegate.CreateDelegate(typeof(Action<HelloWorld>), typeof(HelloWorld).GetMethod(nameof(HelloWorld.HelloWorldInstance)));
open.DynamicInvoke(this);
}
}

同样特别需要注意:

  • 这里创建的委托类型为:typeof(HelloWorld)不是typeof(Action<string>)
  • 可以注意到委托的Target属性为null

原文的例子较难一点,感兴趣的可以通过前文链接查看。接着我们回到原文。

原文中的其他注意点

原文中提供了其他部分需要注意的部分包括

var allNumbers = Enumerable.Range(1, Int32.MaxValue);
Func<int, IEnumerable<int>> countTo = allNumbers.Take;

countTo这里可以作为IEnumerable<int>的实例方法来使用。

  • 作者最后说,除了扩展方法,特殊情况的委托的应用只占很小一部分,但对我们对委托的理解很重要。

me:不明觉厉。以及以上是这个系列的第二篇,有机会希望带大家更深入的理解委托。

最新文章

  1. 程序员装B指南
  2. APP产品交互设计资源汇总(不断更新中...)
  3. java多线程系类:JUC原子类:01之框架
  4. js简单操作Cookie
  5. NYOJ 485
  6. AFNetworking之缓存篇
  7. C语言数据库编程
  8. Linux3.4内核 Yaffs2文件系统的移植
  9. Android核心分析之二十六Android GDI之SurfaceFlinger
  10. Tengine – Nginx衍生版
  11. AIX下解决POWERHA的脑裂问题
  12. QT模态对话框用法(在UI文件中设置Widget背景图,这个图是一个带阴影边框的图片——酷)
  13. windows下搭建Apache+Mysql+PHP开发环境
  14. struts2整合JFreechart 饼图、折线图、柱形图
  15. hdu 6068--Classic Quotation(kmp+DP)
  16. 有var和没有var的本质区别
  17. 【BZOJ2337】Xor和路径(高斯消元)
  18. eMMC基础技术2:eMMC概述
  19. HA主备路由模式的原理 + HA和负载均衡的区别
  20. CorelCAD for Mac(绘图设计软件)破解版安装

热门文章

  1. MySQL基本命令操作及数据库基本概念
  2. docker基础——5.Dockerfile
  3. Sublime Text3安装及汉化
  4. opencv笔记-SimpleBlobDetector
  5. Solution -「CF 1349D」Slime and Biscuits
  6. tep集成mitmproxy录制流量自动生成用例
  7. 使用Java的GUI技术实现 “ 贪吃蛇 ” 游戏
  8. Hbuilderx换行问题(属性合并一行展示)
  9. netty系列之:channelPipeline详解
  10. 忘掉cmd.exe吧!选用优雅的控制台终端(ConsoleZ)