.NET Core 委托原理解析

在 .NET Core 中,委托(Delegate)是一种类型安全的函数指针,它允许你将方法作为参数传递给其他方法,或者将方法存储在变量中以便稍后调用。委托在事件处理、回调机制以及异步编程中非常有用。理解委托的运行原理对于掌握 .NET Core 的高级编程技巧至关重要。

1. 委托的基本概念

委托是一种引用类型,它引用一个或多个方法。委托定义了方法的签名(参数类型和返回类型),因此只有具有相同签名的方法才能被委托引用。

1.1 定义委托

你可以通过 delegate 关键字来定义一个委托类型。例如:

 // 定义一个委托类型
public delegate void GreetDelegate(string name);

这个委托类型 GreetDelegate 可以引用任何具有 void 返回类型和 string 参数的方法。

1.2 实例化委托

一旦定义了委托类型,你可以创建该委托的实例,并将方法赋值给它。例如:

  // 与委托签名匹配的方法
  public void Greet(string name)
  {
      Console.WriteLine($"Hello, {name}!");
  }   // 创建委托实例并绑定方法
  GreetDelegate del  = new GreetDelegate(Greet);

在这个例子中,del 是一个委托实例,它引用了 Greet 方法。

1.3 调用委托

你可以像调用方法一样调用委托:

// 调用委托
del("World");

这会调用 del 方法,并输出 "Hello, World!"

2.委托的类型

1. 单播委托(Singlecast Delegate)

单播委托是指一个委托实例只能引用一个方法。这是最基本的委托类型。

public delegate void GreetDelegate(string message);

public void ShowMessage(string message)
{
    Console.WriteLine(message);
} GreetDelegate del = new GreetDelegate(ShowMessage);
del("Hello, World!"); // 输出:Hello, World!

2. 多播委托(Multicast Delegate)

多播委托是指一个委托实例可以引用多个方法。通过 += 运算符可以将多个方法添加到委托实例中,并通过 -= 运算符移除方法。

public void ShowMessage1(string message)
{
    Console.WriteLine($"Message 1: {message}");
} public void ShowMessage2(string message)
{
    Console.WriteLine($"Message 2: {message}");
} public void ShowMessage3(string message)
{
    Console.WriteLine($"Message 3: {message}");
} GreetMulticastDelegate del = new GreetMulticastDelegate(ShowMessage1);
del += ShowMessage2;
del += ShowMessage3;
del -= ShowMessage2; del("Hello, World!");

在这个例子中,del 委托实例引用了多个方法:将ShowMessage1,ShowMessage2ShowMessage3添加到了多播委托实例中,然后并通过 -= 运算符移除ShowMessage2。然后调用 del("Hello, World!") 时,三个方法都会被调用,输出如下:

Message 1: Hello, World!
//ShowMessage2方法已移除
Message 3: Hello, World!

3. 泛型委托(Generic Delegate)

泛型委托是 C# 中的一种特殊委托类型,它允许你定义可以处理多种数据类型的委托。通过使用泛型,你可以编写更通用、更灵活的代码,而不需要为每种数据类型单独定义委托。

以下是几个泛型委托的示例,展示了如何使用泛型委托处理不同类型的数据。

3.1 简单的泛型委托

public delegate T MyGenericDelegate<T>(T arg);

public int Square(int x)
{
    return x * x;
} public string Reverse(string s)
{
    return new string(s.Reverse().ToArray());
} MyGenericDelegate<int> intDelegate = new MyGenericDelegate<int>(Square);
Console.WriteLine(intDelegate(5)); // 输出:25 MyGenericDelegate<string> stringDelegate = new MyGenericDelegate<string>(Reverse);
Console.WriteLine(stringDelegate("hello")); // 输出:olleh
  • 说明

    • MyGenericDelegate 实例化了一个处理 int 类型数据的委托。
    • MyGenericDelegate 实例化了一个处理 string 类型数据的委托。

3.2 多参数泛型委托

public delegate TResult MyGenericDelegate<T1, T2, TResult>(T1 arg1, T2 arg2);

public int Add(int a, int b)
{
    return a + b;
} public string Concat(string s1, string s2)
{
    return s1 + s2;
} MyGenericDelegate<int, int, int> intDelegate = new MyGenericDelegate<int, int, int>(Add);
Console.WriteLine(intDelegate(3, 5)); // 输出:8 MyGenericDelegate<string, string, string> stringDelegate = new MyGenericDelegate<string, string, string>(Concat);
Console.WriteLine(stringDelegate("Hello, ", "World!")); // 输出:Hello, World!
  • 说明

    • MyGenericDelegate 实例化了一个处理两个 int 类型参数并返回 int 类型结果的委托。
    • MyGenericDelegate 实例化了一个处理两个 string 类型参数并返回 string 类型结果的委托。

4. 内置委托类型

C# 提供了一些内置的泛型委托类型,可以直接使用,而无需自定义委托。

4.1 Action 委托

Action 委托用于引用没有返回值的方法。它可以有 0 到 16 个参数。

Action<string> action = (message) => Console.WriteLine(message);
action("Hello, World!"); // 输出:Hello, World!

4.2 Func 委托

Func 委托用于引用有返回值的方法。它可以有 0 到 16 个参数,最后一个泛型参数是返回值类型。

Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(3, 5)); // 输出:8

4.3 Predicate 委托

Predicate 委托用于引用返回布尔值的方法,通常用于条件判断。

Predicate<int> isEven = (num) => num % 2 == 0;
Console.WriteLine(isEven(4)); // 输出:True

5. 匿名方法委托

匿名方法允许你直接定义委托的实现,而无需显式声明一个方法。

MyDelegate del = delegate(string message)
{
    Console.WriteLine(message);
}; del("Hello, World!"); // 输出:Hello, World!

6. Lambda 表达式委托

Lambda 表达式是一种更简洁的匿名方法写法,通常用于定义委托。

Action<string> action = (message) => Console.WriteLine(message);
action("Hello, World!"); // 输出:Hello, World! Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(3, 5)); // 输出:8

7. 事件委托

事件是一种特殊的委托,通常用于实现观察者模式。事件委托通常与 EventHandlerEventHandler 一起使用。

public class Button
{
    public event EventHandler Click;     public void OnClick()
    {
        Click?.Invoke(this, EventArgs.Empty);
    }
} public class Program
{
    public static void Main()
    {
        Button button = new Button();
        button.Click += (sender, e) => Console.WriteLine("Button clicked!");
        button.OnClick(); // 输出:Button clicked!
    }
}

8. 异步委托

异步委托允许你异步调用方法,通常与 BeginInvokeEndInvoke 一起使用。

public delegate int MyAsyncDelegate(int x, int y);

public int Add(int a, int b)
{
    return a + b;
} MyAsyncDelegate del = new MyAsyncDelegate(Add);
IAsyncResult result = del.BeginInvoke(3, 5, null, null);
int sum = del.EndInvoke(result);
Console.WriteLine(sum); // 输出:8

9. 动态委托

动态委托允许你在运行时动态创建和调用委托。

public int Multiply(int a, int b)
{
    return a * b;
} var method = typeof(Program).GetMethod("Multiply");
var del = Delegate.CreateDelegate(typeof(Func<int, int, int>), null, method);
int result = (del as Func<int, int, int>)(3, 5);
Console.WriteLine(result); // 输出:15

3. 委托的运行原理

委托的运行原理涉及到 .NET Core 的运行时机制和内部实现。以下是委托运行原理的关键点:

3.1 委托的内部结构

在 .NET Core 中,委托是一个类,它继承自 System.MulticastDelegate 类。System.MulticastDelegate 类又继承自 System.Delegate 类。委托类包含以下关键成员:

  • _target:指向调用方法的对象实例(如果是静态方法,则为 null)。
  • _methodPtr:指向方法的函数指针。
  • _invocationList:用于存储多播委托中的多个方法。

3.2 委托的调用

当你调用委托时,.NET Core 运行时会执行以下步骤:

  1. 1. 检查委托实例是否为**** **null**:如果委托实例为 null,则会抛出 NullReferenceException
  2. 2. 调用委托的**** **Invoke** ****方法:委托实例的 Invoke 方法会被调用,该方法会根据 _target_methodPtr 调用实际的方法。
  3. 3. 处理多播委托:如果委托是多播委托(即 _invocationList 不为 null),则 Invoke 方法会遍历 _invocationList,依次调用每个方法。

3.3 委托的优化

.NET Core 对委托的调用进行了优化,以提高性能。例如,对于单播委托(即只引用一个方法的委托),.NET Core 会直接调用方法,而不需要通过 Invoke 方法。

4. 委托的应用场景

委托在 .NET Core 中有多种应用场景,以下是一些常见的场景:

4.1 事件处理

委托在事件处理中非常常见。事件是一种特殊的委托,它允许对象在发生某些事情时通知其他对象。例如:

C#public class Button
{
    public event Action Click;     public void OnClick()
    {
        Click?.Invoke();
    }
} public class Program
{
    public static void Main()
    {
        Button button = new Button();
        button.Click += () => Console.WriteLine("Button clicked!");
        button.OnClick();
    }
}

在这个例子中,Button 类定义了一个 Click 事件,当 OnClick 方法被调用时,事件处理程序会被触发。

4.2 回调机制

委托可以用于实现回调机制,允许一个方法在完成时通知另一个方法。例如:

public void DoWork(Action callback)
{
    // 执行一些工作
    Console.WriteLine("Work is done.");     // 调用回调方法
    callback?.Invoke();
} public void Main()
{
    DoWork(() => Console.WriteLine("Callback called."));
}

在这个例子中,DoWork 方法在完成工作后调用传入的回调方法。

4.3 异步编程

委托在异步编程中也非常有用。例如,Task 类的 ContinueWith 方法允许你在任务完成时执行一个委托:

Task.Run(() => Console.WriteLine("Task is running..."))
    .ContinueWith(task => Console.WriteLine("Task is completed."));

5. 总结

委托是 .NET Core 中一个非常强大的特性,它允许你将方法作为参数传递、存储和调用。理解委托的运行原理有助于你更好地利用这一特性,特别是在事件处理、回调机制和异步编程中。通过掌握委托,你可以编写更加灵活和可扩展的代码。

.NET Core 委托原理解析的更多相关文章

  1. EntityFramework Core表名原理解析,让我来,揭开你神秘的面纱

    前言 上一节我们针对最开始抛出的异常只是进行了浅尝辄止的解析,是不是有点意犹未尽的感觉,是的,我也有这种感觉,看到这里相信您和我会有一些疑惑,要是我们接下来通过注解.Fluent APi.DbSet分 ...

  2. ASP.NET Core 运行原理解剖[2]:Hosting补充之配置介绍

    在上一章中,我们介绍了 ASP.NET Core 的启动过程,主要是对 WebHost 源码的探索.而本文则是对上文的一个补充,更加偏向于实战,详细的介绍一下我们在实际开发中需要对 Hosting 做 ...

  3. [置顶] 滴滴插件化框架VirtualAPK原理解析(一)之插件Activity管理

    上周末,滴滴与360都开源了各自的插件化框架,VirtualAPK与RePlugin,作为一个插件化方面的狂热研究者,在周末就迫不及待的下载了Virtualapk框架来进行研究,本篇博客带来的是Vir ...

  4. ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)

    ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件) Startup Class 1.Startup Constructor(构造函数) 2.Configure ...

  5. ASP.NET Core 运行原理解剖[1]:Hosting

    ASP.NET Core 是新一代的 ASP.NET,第一次出现时代号为 ASP.NET vNext,后来命名为ASP.NET 5,随着它的完善与成熟,最终命名为 ASP.NET Core,表明它不是 ...

  6. ASP.NET Core 运行原理解剖[3]:Middleware-请求管道的构成

    在 ASP.NET 中,我们知道,它有一个面向切面的请求管道,有19个主要的事件构成,能够让我们进行灵活的扩展.通常是在 web.config 中通过注册 HttpModule 来实现对请求管道事件监 ...

  7. ASP.NET Core 运行原理剖析

    1. ASP.NET Core 运行原理剖析 1.1. 概述 1.2. 文件配置 1.2.1. Starup文件配置 Configure ConfigureServices 1.2.2. appset ...

  8. 基于OpenCV进行图像拼接原理解析和编码实现(提纲 代码和具体内容在课件中)

    一.背景 1.1概念定义 我们这里想要实现的图像拼接,既不是如题图1和2这样的"图片艺术拼接",也不是如图3这样的"显示拼接",而是实现类似"BaiD ...

  9. (转)Apache和Nginx运行原理解析

    Apache和Nginx运行原理解析 原文:https://www.server110.com/nginx/201402/6543.html Web服务器 Web服务器也称为WWW(WORLD WID ...

  10. Spring IOC设计原理解析:本文乃学习整理参考而来

    Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. I ...

随机推荐

  1. 使用wxpython开发跨平台桌面应用,实现程序托盘图标和界面最小化及恢复处理

    在前面随笔<基于wxpython的跨平台桌面应用系统开发>介绍了一些关于wxpython开发跨平台桌面应用的总体效果,开发桌面应用,会有很多界面细节需要逐一处理,本篇随笔继续深入该主题,对 ...

  2. 重温c语言之,7天开整,就是随便的写写,第三天+第四天版

    一:指针 1.关于指针的含义---粗略 例如:int a=10; int* p=&a; 这里的*,是说明p是指针变量,int 说明p是指向的对象是int类型的 *p=20, 这里的*是解引用符 ...

  3. linux终端高级玩法详细介绍

    专注于收集整理更多好玩技巧 更改终端命令行颜色 vi /etc/profile PS1='[\[\e[32m\]\u\[\e[0m\]\[\e[35m\]@\[\e[0m\]\[\e[33m\]\h\ ...

  4. 血泪史: k8s Initial timeout of 40s passed.

    背景: k8s不管是 kubeadm init 和join都会报错 kubelet-start] Writing kubelet configuration to file "/var/li ...

  5. 新一代AI换脸更自然,DeepLiveCam下载介绍(可直播)

    DeepLiveCam是一款基于人工智能的图片替换工具,专注于提供实时人脸交换和一键视频深度伪造(deepfake)技术,能通过使用单张图片,在视频或直播中实现高精度的人脸替换 DeepLiveCam ...

  6. 切换自己为www-data用户

    突发奇想的想把切换为www-data用户去看看会怎么样.然后做了一个尝试 由于我安装了lamp环境,所以有www-data用户,用它可以来执行web php ,以及安全放心的跑cli(避免权限过高执行 ...

  7. python模块导入规则(相对导入和绝对导入)

    python模块可以相对导入和绝对导入,但这两者是不能随意替换使用的.本文主要讨论工作目录下模块之间的导入规则.其中相对导入前面有一个'.',表示从该脚本所在目录开始索引,而绝对导入前面没有'.',表 ...

  8. HTTP请求与响应格式解析

     HTTP是Web浏览器与Web服务器之间通信的标准协议,HTTP指明了客户端如何与服务器建立连接,如果从服务器请求数据,服务器如何响应请求,关闭连接.HTTP是使用TCP/IP协议进行传输数据的,也 ...

  9. Redis工具之redis_rdb_tools

    redis_rdb_tools工具的介绍: 解析redis的dump.rdb文件,分析内存,以JSON格式导出数据.|提供的功能有: 1. 生成内存报告 2. 转储文件到JSON 3. 使用标准的di ...

  10. 【FAQ】HarmonyOS SDK 闭源开放能力 —Push Kit(6)

    1.问题描述: 推送通知到手机,怎么配置拉起应用指定的页面? 解决方案: 1.如果点击通知栏打开默认Ability的话, actionType可以设置为0, 同时可以在.clickAction.dat ...