当前位置: 首页 > news >正文

C# 委托的底层实现

C# 委托的底层实现

委托在 C# 中是一个强大的特性,它的底层实现紧密地结合了 .NET 的类型系统和 CLR(公共语言运行时)的机制。

简单来说,委托本质上是一个类,这个类持有一个方法列表(调用列表),并可以安全地调用这些方法。

下面我们分层次来剖析它的底层实现。

1. 核心概念:委托是一个类

当你使用 delegate 关键字定义一个委托类型时,编译器并不会创建一个什么全新的“魔法”类型,而是会在编译时为你生成一个继承自 System.MulticastDelegate 的类

System.MulticastDelegate 本身又继承自 System.Delegate。这两个类是 .NET 框架中所有委托类型的基类,它们提供了委托的核心能力。

类层次结构:
object -> System.Delegate -> System.MulticastDelegate -> [你定义的委托类型,如 MyDelegate]

2. 编译器为我们生成了什么?

我们通过一个例子来看。假设你定义了如下委托和方法:

// 1. 定义一个委托类型
public delegate int MyDelegate(string message);// 2. 一个匹配的方法
public static int MyMethod(string msg)
{Console.WriteLine(msg);return msg.Length;
}

编译后,编译器会为 MyDelegate 生成一个类似于下面的类(这是概念上的示意,并非实际代码):

public sealed class MyDelegate : System.MulticastDelegate
{// 1. 构造函数// 接收一个对象实例和方法指针public MyDelegate(object @object, IntPtr methodPtr);// 2. 与委托定义具有相同签名的方法:Invoke// 这是你直接调用委托时(如 del("Hello"))实际调用的方法public virtual int Invoke(string message);// 3. 异步编程模型(APM)的 BeginInvoke 和 EndInvoke// 用于异步执行委托(注意:在 .NET Core 及更高版本中,此模式已被 Task 取代,且在某些平台上不被支持)public virtual IAsyncResult BeginInvoke(string message, AsyncCallback callback, object state);public virtual int EndInvoke(IAsyncResult result);
}

关键成员解析:

  • Invoke 方法:这是委托的核心。它的签名与你定义委托时完全一致。当你写 myDelegate("Hello") 时,C# 编译器会将其编译为对 myDelegate.Invoke("Hello") 的调用。这个方法负责执行委托调用列表中的所有方法。
  • BeginInvoke / EndInvoke:这是基于 IAsyncResult 的旧异步模式。它利用线程池来异步执行 Invoke 方法。重要提示:从 .NET Core 开始,这个模式不再被推荐使用,并且在非 Windows 平台可能不被支持。现代的异步编程应使用 async/awaitTask
  • 构造函数:它接收两个关键参数:
    • object @object:对于实例方法,这是方法所属的对象实例(this)。对于静态方法,这个参数为 null
    • IntPtr methodPtr:一个指向方法内存地址的指针。这是一个托管指针,它标识了要执行的具体方法。

3. 内存布局与关键字段

System.MulticastDelegate 内部(其字段定义在 System.Delegate 中),有几个至关重要的字段:

  • _target (object):目标对象。如果是绑定实例方法,这里存储的就是那个对象实例。如果是静态方法,这里为 null
  • _methodPtr (IntPtr):方法指针。这是一个内部标识符,指向要调用的方法。
  • _invocationList (object):调用列表。这是实现多播委托的关键。

单播 vs. 多播

  • 单播委托:当一个委托只封装一个方法时,_invocationListnull。调用 Invoke 时,直接使用 _target_methodPtr 来调用那个单一方法。
  • 多播委托:当你使用 += 操作符组合多个委托时(例如 myDelegate += AnotherMethod),_invocationList 就不再是 null。它会变成一个委托数组(System.Delegate[]),按顺序包含了所有要调用的方法。当调用 Invoke 时,委托会遍历这个数组,依次调用其中的每一个方法。

4. 委托的实例化与绑定

当你将方法分配给委托时:

MyDelegate del = MyMethod; // 静态方法
// 或者
MyClass obj = new MyClass();
MyDelegate del2 = obj.InstanceMethod; // 实例方法

编译器会生成代码来实例化你的委托类,并传入相应的 _target_methodPtr

  • 对于 MyMethod(静态方法):_target = null, _methodPtr 指向 MyMethod
  • 对于 obj.InstanceMethod(实例方法):_target = obj, _methodPtr 指向 MyClass.InstanceMethod

5. 性能考量与优化

  • 内存分配:创建一个新的委托实例是一个在堆上进行的操作,因为它是一个 class。这意味者频繁创建和丢弃委托会产生垃圾回收压力。
  • 调用开销:与直接方法调用相比,委托调用有一个微小的间接层。它需要先找到 Invoke 方法,然后通过它间接调用目标方法。但在绝大多数场景下,这种开销可以忽略不计。
  • 缓存:如果一个委托会被重复使用,最好将其缓存起来,避免重复实例化。

现代 C# 的补充:FuncAction

从 .NET Framework 3.5 开始,引入了泛型委托 Func<...>(有返回值)和 Action<...>(无返回值)。它们覆盖了大部分常用的方法签名。

// 你自己定义的委托
public delegate int StringProcessor(string input);// 可以用内置的 Func 代替
Func<string, int> stringProcessor;

使用它们可以避免到处声明委托类型,使代码更简洁。它们的底层实现机制与自定义委托类型完全相同。

总结

特性底层实现
类型一个继承自 System.MulticastDelegate 的密封类。
调用编译为调用生成的 Invoke 方法。
方法绑定通过构造函数存储 _target(对象实例)和 _methodPtr(方法指针)。
多播通过 _invocationList 字段(一个委托数组)实现,调用时遍历数组。
异步(旧)通过生成的 BeginInvoke/EndInvoke 方法(已过时)。
本质类型安全的、面向对象的函数指针

理解委托的底层实现有助于你更深刻地认识到它并非语法糖,而是一个具有完整类型系统支持的、功能强大的运行时特性。它也是 C# 中事件、Lambda 表达式和 LINQ 等技术的基础。

http://www.dtcms.com/a/472186.html

相关文章:

  • 北京icp网站备案张家港城市建设规划局网站
  • 网站建设做网站费用txt怎么做pdf电子书下载网站
  • 深圳沙井做网站内蒙网
  • 网站建设数据库实训体会wordpress竞猜插件
  • 上海有名的做网站的公司有哪些零食店网站构建策划报告
  • 大多数软件仍然是定制开发的想做个卷帘门百度优化网站
  • 编程和做网站那个号网站数据库默认地址
  • wordpress授权怎么破解版湖南网络优化
  • NetworkManager服务详解
  • 样式网站wordpress商业主体
  • 镇江seo网站效果型网站
  • 三大门户网站网络服务商在哪
  • 网站建设套餐表怎么分析网页界面设计
  • 做网站推广的一般都是什么公司本溪网站开发
  • 项目网站分析怎么做网站导航地图
  • 网站建设计入哪个科目中国建设基础设施公司网站
  • 集团网站哪可以免费设计装修房子
  • 做网站什么分类流量多优化网站的方法
  • 计算机操作系统:进程控制
  • 自贡移动网站建设网站修改域名服务器
  • phpcms 企业网站游戏优化软件
  • 广州做网站的企业wordpress添加社交媒体链接
  • MySQL修改字段长度失败,如何快速还原减少损失?
  • wordpress 营销模板厦门网站seo
  • 做新网站推广的活动成都网络推广公司
  • 苏州做网站seo关键词布局案例
  • 章丘做网站php网站开发与设计
  • 郑州市金水区建设局官方网站福田网络推广公司
  • 郑州做网站 码通建设手机银行app下载
  • 网站qq微信分享怎么做的co域名哪些网站