浅析委托的执行机制

浅析委托的执行机制

前面的一篇文章,介绍了委托的基本使用,和委托带来的好处,下面,我们再来更加深入的了解一下委托。

饮水思源

《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();.

当我们声明一个委托以后,会自动根据我们委托的类型生成以上的三个方法,,其中BeginInvokeEndInvoke 方法,是在异步操作的时候使用的不是我们本文讨论的范围。

调用委托

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 类。 当我们声明一个委托的时候,会自动生成一些方法,最后系统会通过,反射的机制,来执行我们封装好的方法。

posted @ 2017-09-12 11:24  鲁迅认识的那只猹  阅读(194)  评论(0编辑  收藏  举报