dotnet 委托的实现解析(2)开放委托和封闭委托 (Open Delegates vs. Closed Delegates)
前言
这是个人对委托的理解系列第二篇,部分翻译自 Open Delegates vs. Closed Delegates – SLaks.Blog,好像还没人翻译过,加上部分个人理解。希望能对大家理解委托有所帮助。
正文
.Net支持两种委托:开放委托和封闭委托。open delegates
和 closed 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
。
原文的例子较难一点,感兴趣的可以通过前文链接查看。接着我们回到原文。
原文中的其他注意点
原文中提供了其他部分需要注意的部分包括
- 不能使用值类型作为第一个参数创建静态方法的封闭委托。参考链接c# - Extension methods defined on value types cannot be used to create delegates - Why not? - Stack Overflow
- C# 3对创建封闭委托提供了部分语法支持,你可以从扩展方法创建委托,就好像它是它所扩展的类型的实例方法一样 例如:
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)的更多相关文章
- 【详细】【转】C#中理解委托和事件 事件的本质其实就是委托 RabbitMQ英汉互翼(一),RabbitMQ, RabbitMQ教程, RabbitMQ入门
[详细][转]C#中理解委托和事件 文章是很基础,但很实用,看了这篇文章,让我一下回到了2016年刚刚学委托的时候,故转之! 1.委托 委托类似于C++中的函数指针(一个指向内存位置的指针).委托 ...
- 用五分钟重温委托,匿名方法,Lambda,泛型委托,表达式树
这些对老一代的程序员都是老生常谈的东西,没什么新意,对新生代的程序员却充满着魅力.曾经新生代,好多都经过漫长的学习,理解,实践才能掌握委托,表达式树这些应用.今天我尝试用简单的方法叙述一下,让大家在五 ...
- 转帖:用五分钟重温委托,匿名方法,Lambda,泛型委托,表达式树
用五分钟重温委托,匿名方法,Lambda,泛型委托,表达式树 这些对老一代的程序员都是老生常谈的东西,没什么新意,对新生代的程序员却充满着魅力.曾经新生代,好多都经过漫长的学习,理解,实践才能掌握委托 ...
- C#中使用委托、接口、匿名方法、泛型委托实现加减乘除算法
使用C#实现加减乘除算法经常被用作新手练习.本篇来分别体验通过委托.接口.匿名方法.泛型委托来实现. 使用委托实现 加减乘除拥有相同的参数个数.类型和返回类型,首先想到了使用委托实现. //创建一个委 ...
- 委托,匿名方法,Lambda,泛型委托,表达式树
一.委托:完成一个委托应分三个步骤://step01:首先用delegate定义一个委托;public delegate int CalculatorAdd(int x, int y);//step0 ...
- 委托、Lambda表达式、事件系列04,委托链是怎样形成的, 多播委托, 调用委托链方法,委托链异常处理
委托是多播委托,我们可以通过"+="把多个方法赋给委托变量,这样就形成了一个委托链.本篇的话题包括:委托链是怎样形成的,如何调用委托链方法,以及委托链异常处理. □ 调用返回类型为 ...
- 委托、Lambda表达式、事件系列01,委托是什么,委托的基本用法,委托的Method和Target属性
委托是一个类. namespace ConsoleApplication1 { internal delegate void MyDelegate(int val); class Program { ...
- c#打包文件解压缩 C#中使用委托、接口、匿名方法、泛型委托实现加减乘除算法 一个简单例子理解C#的协变和逆变 对于过长字符串的大小比对
首先要引用一下类库:using Ionic.Zip;这个类库可以到网上下载. 下面对类库使用的封装方法: 得到指定的输入流的ZIP压缩流对象 /// <summary> /// 得到指定的 ...
- C#委托,匿名方法,Lambda,泛型委托,表达式树代码示例
第一分钟:委托 有些教材,博客说到委托都会提到事件,虽然事件是委托的一个实例,但是为了理解起来更简单,今天只谈委托不谈事件.先上一段代码: 下边的代码,完成了一个委托应用的演示.一个委托分三个步骤: ...
随机推荐
- pyrealsense2学习
如何得到realsense设备信息 前提:将D455连接在电脑上,并且已经下载好 Realsense Viewer 打开Realsense Viewer--> Info, 便可得到相机的一些参数 ...
- Pandas常用操作 - 新增数据列
初始化测试数据 df = pd.DataFrame({'stu_name': ['Nancy', 'Tony', 'Tim', 'Jack', 'Lucy'], 'stu_age': [17, 16, ...
- 我的新书——《PHP程序员面试笔试宝典》
你好,是我琉忆. 一个文艺的PHP开发工程师. 很荣幸能够在这里带来我的第一本新书--<PHP程序员面试笔试宝典>. 一.创作过程 <PHP程序员面试笔试宝典>是我的第一本书, ...
- Solution -「NOI 2018」「洛谷 P4768」归程
\(\mathcal{Description}\) Link. 给定一个 \(n\) 个点 \(m\) 条边的无向连通图,边形如 \((u,v,l,a)\).每次询问给出 \(u,p\),回答 ...
- 【故障公告】k8s 开船记:增加控制舱(control-plane)造成的翻船
春节期间我们更换了 kubernetes 生产集群,旧集群的 kubernetes 版本是 1.17.0,新集群版本是 1.23.3,新集群上部署了 dapr,最近准备将更多独立部署的服务器部署到 k ...
- XStream: Stream Processing Platform at Facebook
这是Facebook在FlinkForward2021上的一个talk, 主题如下 在前面的论文中分析了Facebook的实时计算引擎的设计和选型的考量,里面提到了Facebook的实时计算引擎为了满 ...
- 【Kotlin】初识Kotlin(二)
[Kotlin]初识Kotlin(二) 1.Kotlin的流程控制 流程控制是一门语言中最重要的部分之一,从最经典的if...else...,到之后的switch,再到循环控制的for循环和while ...
- Element-UI整合VUE下拉选项无法选中的一个小问题
searchObj: { subjectId: ''// 解决查询表单无法选中二级类别,必须要现在模型中给一个空的初始值 },
- excel写入数值型内容
一开始的想法是设置单元格格式为数值型 XSSFDataFormat format= (XSSFDataFormat) workbook.createDataFormat(); CellStyle cs ...
- DH密钥交换协议
密钥交换 密钥交换简单点来说就是允许两名用户在公开媒体上交换信息以生成"一致"的.可以共享的密钥.也就是由甲方产出一对密钥(公钥.私钥),乙方依照甲方公钥产生乙方密钥对(公钥.私钥 ...