前言

这是个人对委托的理解系列第二篇,部分翻译自 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:不明觉厉。以及以上是这个系列的第二篇,有机会希望带大家更深入的理解委托。

dotnet 委托的实现解析(2)开放委托和封闭委托 (Open Delegates vs. Closed Delegates)的更多相关文章

  1. 【详细】【转】C#中理解委托和事件 事件的本质其实就是委托 RabbitMQ英汉互翼(一),RabbitMQ, RabbitMQ教程, RabbitMQ入门

    [详细][转]C#中理解委托和事件   文章是很基础,但很实用,看了这篇文章,让我一下回到了2016年刚刚学委托的时候,故转之! 1.委托 委托类似于C++中的函数指针(一个指向内存位置的指针).委托 ...

  2. 用五分钟重温委托,匿名方法,Lambda,泛型委托,表达式树

    这些对老一代的程序员都是老生常谈的东西,没什么新意,对新生代的程序员却充满着魅力.曾经新生代,好多都经过漫长的学习,理解,实践才能掌握委托,表达式树这些应用.今天我尝试用简单的方法叙述一下,让大家在五 ...

  3. 转帖:用五分钟重温委托,匿名方法,Lambda,泛型委托,表达式树

    用五分钟重温委托,匿名方法,Lambda,泛型委托,表达式树 这些对老一代的程序员都是老生常谈的东西,没什么新意,对新生代的程序员却充满着魅力.曾经新生代,好多都经过漫长的学习,理解,实践才能掌握委托 ...

  4. C#中使用委托、接口、匿名方法、泛型委托实现加减乘除算法

    使用C#实现加减乘除算法经常被用作新手练习.本篇来分别体验通过委托.接口.匿名方法.泛型委托来实现. 使用委托实现 加减乘除拥有相同的参数个数.类型和返回类型,首先想到了使用委托实现. //创建一个委 ...

  5. 委托,匿名方法,Lambda,泛型委托,表达式树

    一.委托:完成一个委托应分三个步骤://step01:首先用delegate定义一个委托;public delegate int CalculatorAdd(int x, int y);//step0 ...

  6. 委托、Lambda表达式、事件系列04,委托链是怎样形成的, 多播委托, 调用委托链方法,委托链异常处理

    委托是多播委托,我们可以通过"+="把多个方法赋给委托变量,这样就形成了一个委托链.本篇的话题包括:委托链是怎样形成的,如何调用委托链方法,以及委托链异常处理. □ 调用返回类型为 ...

  7. 委托、Lambda表达式、事件系列01,委托是什么,委托的基本用法,委托的Method和Target属性

    委托是一个类. namespace ConsoleApplication1 { internal delegate void MyDelegate(int val); class Program { ...

  8. c#打包文件解压缩 C#中使用委托、接口、匿名方法、泛型委托实现加减乘除算法 一个简单例子理解C#的协变和逆变 对于过长字符串的大小比对

    首先要引用一下类库:using Ionic.Zip;这个类库可以到网上下载. 下面对类库使用的封装方法: 得到指定的输入流的ZIP压缩流对象 /// <summary> /// 得到指定的 ...

  9. C#委托,匿名方法,Lambda,泛型委托,表达式树代码示例

    第一分钟:委托 有些教材,博客说到委托都会提到事件,虽然事件是委托的一个实例,但是为了理解起来更简单,今天只谈委托不谈事件.先上一段代码: 下边的代码,完成了一个委托应用的演示.一个委托分三个步骤: ...

随机推荐

  1. linux 定时删除图以及crontab介绍

    执行 sudo crontab -e 0 3 1 * * /etc/letsencrypt/certbot-auto renew --renew-hook "sudo nginx -s re ...

  2. opencv笔记--stitching模块

    opencv 提供了全景图像拼接的所有实现,包括: 1)stitching 模块提供了图像拼接过程中所需要的基本元素,该模块主要依赖于 features2d 模块: 2)提供了 stitching_d ...

  3. Solution -「CF 232E」Quick Tortoise

    \(\mathcal{Description}\)   Link.   在一张 \(n\times m\) 的网格图中有空格 . 和障碍格 #,\(q\) 次询问,每次查询从 \((x_1,y_1)\ ...

  4. Solution -「HDU 1788」CRT again

    \(\mathcal{Description}\)   Link.   解同余方程组: \[x\equiv m_i-a\pmod{m_i} \]   其中 \(i=1,2,\dots,n\).   \ ...

  5. ansible手动添加模块

    文章目录 安装ansible 验证ansible版本 定义ansible配置文件路径 为ansible添加模块 由于使用pip安装的ansible,自带的模块会比较少,有的模块会不存在,需要自己手动添 ...

  6. 云原生 PostgreSQL 集群 - PGO:来自 Crunchy Data 的 Postgres Operator

    使用 PGO 在 Kubernetes 上运行 Cloud Native PostgreSQL:来自 Crunchy Data 的 Postgres Operator! Cloud Native Po ...

  7. 【Windows 操作系统】Windows 进程的内核对象句柄表

    总结: 1.句柄就是进程句柄表中的索引.2.句柄是对进程范围内一个内核对象地址的引用,一个进程的句柄传给另一个进程是无效的.一个内核对象可用有多个句柄.Windows之所以要设立句柄,根本上源于内存管 ...

  8. 用MySQL碰到的一些“坑”

    本篇文章持续更新. 这里说坑,也不算坑,只是对我一个经常用SQL Server的来说有点不习惯而已. 一.GroupBy 的不同 create table Customer ( CustomerNum ...

  9. pandas补充(其二)与matplotlib补充

    今日内容概要 pandas补充知识(2) matplotlib补充知识 今日内容详细 pandas补充 数据汇总 # 数据透视表 pd.pivot_table(data,values-None,ind ...

  10. 01--c实现基础客户端和服务端与c++ boost.asio实现对比

    c实现服务端和客户端交互: 学习查阅的博客: https://blog.csdn.net/u011068702/article/details/54380259 https://blog.csdn.n ...