浅析委托的执行机制
浅析委托的执行机制
前面的一篇文章,介绍了委托的基本使用,和委托带来的好处,下面,我们再来更加深入的了解一下委托。
饮水思源
《C#本质论》
Overview
首先,明确一点委托是一个类,只不过有些特殊. , 我们来看一下委托的继承层次:
我们新建一个委托:
public delegate void SimpleDelegate(string value);
我们将项目非那放到 ILSpy
中进行一下反编译,并找到我们建立的委托,查看IL代码:
发现: 我们自定义的
Simple
delegate 继承自MulticastDelegate
, 在VS中对MulticastDelegate
进行转到定义操作就会发现MulticastDelegate
继承自Delegate
,通过我们的委托的继承层次就明了了。
Note:
Delegate类中有两个值得我们注意的属性,我们来看一下官方文档.
- MethodInfo Method
关于 Method 的描述是: Gets the method represented by the delegate. 简单的翻译为:获取委托方法的描述
MethodInfo 类,会在反射的知识点中提到。 - object Target
官方文档的描述为: Gets the class instance on which the current delegate invokes the instance method. 获取委托中方法所在对象的类的实例,如果委托封装的方法是一个静态的方法,那么这值为null.
上面的一段解释,可能有点抽闲,比如说, 一个非静态类 Person
中有个方法被封装到了委托中,那么Target的值,就是Person类的实例。
如果,对反射机制有一定的了解,那么我想看到这里,你一定有所领悟,委托最后会通过反射机制来执行,我们所封装的方法。
PS: 这里涉及到了一些反射的知识点,可能看官你可能对反射还没有什么了解,也没关系,这里只需要有一个简单的认识就行。
实例化方法实现委托,和静态方法实现委托的区别
自定义委托会生成一个类,并且生成了Invoke等方法
继续看我们上面的那个委托的反编译后出现的成员。
- Invoke(string);
- BeginInvoke();
- EndInvoke();.
当我们声明一个委托以后,会自动根据我们委托的类型生成以上的三个方法,,其中
BeginInvoke
和EndInvoke
方法,是在异步操作的时候使用的不是我们本文讨论的范围。
调用委托
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnderstandDelegate
{
public delegate void SimpleDelegate(string value);
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnderstandDelegate
{
class Program
{
static void Main(string[] args)
{
TestMethod(Simple);
}
public static void TestMethod(SimpleDelegate sd)
{
sd("HelloWorld!");
}
public static void Simple(string value)
{
Console.WriteLine(value);
}
}
}
当我们在 TestMethod方法中调用委托的时候,我们来看一下IL中间语言的代码:
.method public hidebysig static
void TestMethod (
class UnderstandDelegate.SimpleDelegate sd
) cil managed
{
// Method begins at RVA 0x2065
// Code size 14 (0xe)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldstr "HelloWorld!"
IL_0007: callvirt instance void UnderstandDelegate.SimpleDelegate::Invoke(string)
IL_000c: nop
IL_000d: ret
} // end of method Program::TestMethod.method public hidebysig static
void TestMethod (
class UnderstandDelegate.SimpleDelegate sd
) cil managed
{
// Method begins at RVA 0x2065
// Code size 14 (0xe)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldstr "HelloWorld!"
IL_0007: callvirt instance void UnderstandDelegate.SimpleDelegate::Invoke(string)
IL_000c: nop
IL_000d: ret
} // end of method Program::TestMethod
看到下面这一段,就会发现,当我们通过 sd("HelloWorld!"); 执行委托的时候,最后编译器,还是会为我们编译成 Delegate.Invoke() 的形式,只不过为了我们写代码的方便,我们可以这样的方式来减少代码的书写量。
IL_0007: callvirt instance void UnderstandDelegate.SimpleDelegate::Invoke(string)
委托是不可变的
委托,和我们的string 类型一样,已经创建不能被更改,对委托的任何更改都会创建新的委托对象。
总结
委托本质其实是一个特殊的类而且具有不可变性,并且这个类继承自
MulticastDelegate
,MulticastDelegate
类 继承自Delegate
类。 当我们声明一个委托的时候,会自动生成一些方法,最后系统会通过,反射的机制,来执行我们封装好的方法。