zoukankan      html  css  js  c++  java
  • 浅析委托的执行机制

    浅析委托的执行机制

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

    饮水思源

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

  • 相关阅读:
    解决用ADODB对Access数据库进行操作的一个问题
    简单的多线程操作示例
    线程池操作示例
    日常五大习惯有助减肥 生活至上,美容至尚!
    全面分析男性护肤三大误区 生活至上,美容至尚!
    看了这篇你肯定瘦 全身上下想瘦哪就瘦哪 生活至上,美容至尚!
    护肤:食盐美容4招 控油除痘去黑头 生活至上,美容至尚!
    神奇瘦身食谱 调整你多余脂肪 生活至上,美容至尚!
    一天4时段喝水轻松瘦身 生活至上,美容至尚!
    营养瘦身第一菜——金陵素什锦 生活至上,美容至尚!
  • 原文地址:https://www.cnblogs.com/slyfox/p/7509151.html
Copyright © 2011-2022 走看看