前言

这是个人对委托的理解系列第二篇,部分翻译自 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. shell脚本一键部署——Redis(直接复制执行)亲测100% 有效

    首先所需软件包拖进目录,然后编辑一个脚本,复制一下脚本,source执行一下即可 #!/bin/bash echo -e "\033[31m =====正在验证当前为仅主机还是NAT模式== ...

  2. 虫师Selenium2+Python_3、Python基础

    P38--Python哲学 打开Python shell,输入import this,会看到下面的话: The Zen of Python, by Tim Peters   Beautiful is ...

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

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

  4. jenkins发布代码选择不同分支

    jenkins上传代码分支以前都是用变量的方式,手动实现.过程就像这样 构建时候的界面就像下面这样,需要手动输入分支版本. 或者有固定的上线分支,用参数化构建 选项参数 来选择.总之这些方法比较传统, ...

  5. 数据库项目部署(nginx)

    1.在虚拟机2008server下载nginx http://nginx.org/download/nginx-1.17.10.zip 注1:此版本为window版本 linux版本 2. 解压软件至 ...

  6. CobaltStrike逆向学习系列(7):Controller 任务发布流程分析

    这是[信安成长计划]的第 7 篇文章 关注微信公众号[信安成长计划] 0x00 目录 0x01 Controller->TeamServer 0x02 TeamServer->Beacon ...

  7. 【第一期百题计划进行中,快来打卡学习】吃透java、细化到知识点的练习题及笔试题,助你轻松搞定java

    [快来免费打卡学习]参与方式 本期百题计划开始时间:2022-02-09,今日打卡题已在文中标红. 0.本文文末评论区打卡,需要登录才可以打卡以及查看其他人的打卡记录 1.以下练习题,请用对应的知识点 ...

  8. [Matlab]二维统计分析图实例

    常见的二维统计分析图形: bar(x,y,选项) 条形图 stairs(x,y,选项) 阶梯图 stem(x,y,选项) 杆图 fill(x1,y1,选项1,x2,y2,选项2,--) 填充图 实例: ...

  9. 简单excel饼状图怎么做,bi工具怎么做饼状图

    饼状图是为了在一个整体体现个体所占的比例,比如一块蛋糕每人各分多大份.了解了饼状图的含义,就来学习饼状图怎么做吧. 首先,我们准备excel表格饼状图的初始数据 然后选择excel表格上方的插入,选择 ...

  10. LSMT 实战-python

    长短期记忆网络(LSTM,Long Short-Term Memory) 使用kears 搭建一个LSTM预测模型,使用2022年美国大学生数学建模大赛中C题中处理后的BTC比特币的数据进行数据训练和 ...