.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. 几张图带你了解.NET String

    String 字符串作为一种特殊的引用类型,是迄今为止.NET程序中使用最多的类型.可以说是万物皆可string 因此在分析dump的时候,大量字符串对象是很常见的现象 string的不可变性 str ...

  2. 基于Material Design风格开源、免费的WinForms UI控件库

    前言 今天大姚给大家分享一个基于 Google 的 Material Design 风格开源.免费的.NET WinForms UI控件库:MaterialSkin. WinForms介绍 WinFo ...

  3. spring生态体系

    spring boot使用默认开发配置来实现快速开发spring xd用来简化大数据应用开发spring cloud为分布式系统开发提供工具集spring data对主流的关系型和nosql数据库的支 ...

  4. Go语言编写单元测试用例

    Go单元测试示例 example/ |--division.go |--division_test.go 为什么被测试文件和测试文件通常放到同一个文件夹下以及同一个声明包里 通常情况下,我们把被测试的 ...

  5. NOIP2024模拟11:忠于自我

    NOIP2024模拟11:忠于自我 T1 一句话题意:有若干个容量为 \(L\) 的包,从左往右装物品,当前包还能装则装,否则必须重开一个包装进去,对于\(\forall i \in [1,n]\), ...

  6. Python安装技术类库模块

    方法1: 方法2: 用如下命令安装即可(注意都得是英文字符): # 简单粗暴,但是可能安装到了不同的环境 pip install some-package # 复杂但是精准还快速 C:\Python3 ...

  7. MySQL造数据,批量插入数据脚本

    新建表 create table bigdata( name varchar(32), age int(32), createTime datetime); MySQL批量插入数据脚本 #!/bin/ ...

  8. QT5.15.2 连接MySQL 驱动问题解决方案,无论菜鸟🐦️还是老鸟🦜,解决了就是好鸟🦚

    最新在学QT,现在QT只能在线安装了,用了几天,看到数据库时,需要用MySQL,结果出现了问题. QSqlDatabase: QMYSQL driver not loaded. QSqlDatabas ...

  9. php 版本升级后需要对代码进行兼容性检测

    来到需要检测代码的目录下 需要提前安装 docker 执行 docker run --rm -v $(pwd):/app vfac/php7compatibility 7.3 . --ignore=v ...

  10. WiFi基础(八):WiFi安全、认证与加密

    liwen01 2024.11.17 前言 计算机网络在给人们带来便利的同时,也引入了安全风险,对于无线WiFi网络而言,风险更高.无线 WiFi 网络安全主要包括两大部分:接入认证和数据加密. 虽然 ...