dotnet|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)
因为,"a"
做为第一个参数已经是委托的Target
属性了。
public class HelloWorld
{public void HelloWorldInstance() => Console.WriteLine("hello world");
public void Main()
{
var open = Delegate.CreateDelegate(typeof(Action), typeof(HelloWorld).GetMethod(nameof(HelloWorld.HelloWorldInstance)));
open.DynamicInvoke(this);
}
}
同样特别需要注意:
- 这里创建的委托类型为:
typeof(HelloWorld)
不是typeof(Action)
- 可以注意到委托的
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> countTo = allNumbers.Take;
countTo
这里可以作为IEnumerable
的实例方法来使用。- 作者最后说,除了扩展方法,特殊情况的委托的应用只占很小一部分,但对我们对委托的理解很重要。
【dotnet|dotnet 委托的实现解析(2)开放委托和封闭委托 (Open Delegates vs. Closed Delegates)】me:不明觉厉。以及以上是这个系列的第二篇,有机会希望带大家更深入的理解委托。
推荐阅读
- Ubuntu系统中防火墙的使用和开放端口
- S 锁与 X 锁,当前读与快照读!
- java|一位群友作为后端开发在滴滴和头条分别干了 2 年的经验总结
- 教你用认知和人性来做最棒的程序员
- python基础之序列类型的方法——字符串方法
- SpringBoot|解决决SpringBoot图片上传需重启服务器才能显示的问题
- Java基础的查漏补缺|仿生Power Designer会梦到Mysql电子羊吗?(Power Designer教程)
- 分布式|分布式 PostgreSQL 集群(Citus),分布式表中的分布列选择最佳实践
- 3.21|3.21 第二周学习成果
- 这个牛逼的IDE插件,让你轻松检测代码安全漏洞,一键修复,核心引擎已开源