.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. 结构体(C语言)

    目录 1. 结构体类型的声明 1.1 结构体回顾 1.1.1 结构的声明 1.1.2 结构体变量的创建和初始化 1.2 结构的特殊声明 1.3 结构的自引用 2. 结构体内存对齐 2.1 对齐规则 2 ...

  2. 字符串、列表、元组、字典(python)

    文章目录 1.python字符串 1.1 python访问字符串中的值 1.2Python 字符串连接 1.3Python字符串运算符 2.python列表 2.1访问列表中的值 2.2更新列表 2. ...

  3. 有Redis为什么还要本地缓存?谈谈你对本地缓存的理解?

    本地缓存是将数据存储在应用程序所在的本地内存中的缓存方式.既然,已经有了 Redis 可以实现分布式缓存了,为什么还需要本地缓存呢?接下来,我们一起来看. 为什么需要本地缓存? 尽管已经有 Redis ...

  4. 题解: CF768D Jon and Orbs

    题解:CF768D Jon and Orbs 一句话题面:有k种不同的物品,每天等概率任取一种(不一定是新的种类).q组询问,每组给出一个p,问取完这k件物品的概率不小于\(\frac{p}{2000 ...

  5. Python中序列化/反序列化JSON格式的数据

    基本概念 JSON: JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式.简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言. 易于 ...

  6. Slate文档编辑器-WrapNode数据结构与操作变换

    Slate文档编辑器-WrapNode数据结构与操作变换 在之前我们聊到了一些关于slate富文本引擎的基本概念,并且对基于slate实现文档编辑器的一些插件化能力设计.类型拓展.具体方案等作了探讨, ...

  7. 卡特兰数 Catalan 数列

    卡特兰数 Catalan 数列 引入 有一个无限大的栈,进栈的顺序为 \(1,2,\cdots,n\),求有多少种不同的出栈序列. 设 \(h[n]\) 为 \(n\) 个数的出栈序列方案数. 可以这 ...

  8. ScheduledThreadPoolExecutor与System#nanoTime

    一直流传着Timer使用的是绝对时间,ScheduledThreadPoolExecutor使用的是相对时间,那么ScheduledThreadPoolExecutor是如何实现相对时间的? 先看看S ...

  9. Java Study For Five Day( 面向对象一)

    面向对象 1.面向对象的概念 2.理解面向对象 *面向对象其实是相对面向过程而言的,面向对象和面向过程都是一种思想,它们所强调的内容不一样. *面向对象:强调的是功能的行为,将功能进行了封装成了对象, ...

  10. Blazor 组件库 BootstrapBlazor 中Circle组件介绍

    组件介绍 Circle进度环组件,是一个图表类组件.一般有两种用途: 显示某项任务进度的百分比. 统计某些指标的占比. 它的样子如下: 它的代码如下: <Circle Width="2 ...