.NET Core 委托原理解析

.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)); // 输出:25MyGenericDelegate<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)); // 输出:8MyGenericDelegate<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 中一个非常强大的特性,它允许你将方法作为参数传递、存储和调用。理解委托的运行原理有助于你更好地利用这一特性,特别是在事件处理、回调机制和异步编程中。通过掌握委托,你可以编写更加灵活和可扩展的代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/868697.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ellyn-Golang调用级覆盖率&方法调用链插桩采集方案

在应用程序并行执行的情况下,精确获取单个用例、流量、单元测试走过的方法链(有向图)、出入参数、行覆盖等运行时数据,经过一定的加工之后,应用在覆盖率、影响面评估、流量观测、精准测试、流量回放、风险分析等研发效能相关场景。词语解释Ellyn要解决什么问题? 在应用程…

[megatron代码阅读] 1. 初始化和组网

以pretrain_gpt.py为例, 看megatron的整体逻辑. 本章主要包括megatron初始化相关逻辑, 核心函数为initialize_megatron, setup_model_and_optimizer两个 initialize_megatron parse_args 从argparse中直接读取超参数配置. 如学习率, 正则化等. 从环境变量中获取rank等 load_arg…

巧用VTable打造炫酷金字塔图表

在数据分析和可视化领域,表格是展示数据直观、有效的方式之一。今天,就让我们来探索如何利用VTable这个强大的表格组件,制作出既美观又富有信息量的金字塔图表,以及深入了解VTable中各种单元格类型的使用方法,让你的表格也能“绘”出精彩图表!在数据分析和可视化领域,表…

基于 Performace 分析事件循环

我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。本文作者:千寻什么是事件循环? 我们为什么需要事件循环?对于 JavaScript 是一门单线程语言我们是肯定的,JavaScript 单线程的特性保证了渲…

万字图文:SaaS业务架构、价值流、业务能力、业务流程、业务对象、组织架构

大家好,我是汤师爷~ 本文为读者提供一个SaaS业务架构的系统性框架,探讨业务架构分析的核心要素,帮助SaaS企业深入剖析目标客户的业务模式,全面理解他们的业务架构。 无论你是SaaS创业者、产品经理还是架构师,本文内容都将为你的系统设计和决策提供帮助。 1 目标与步骤 Saa…

老奶奶看了都会的WSL2连接USB设备教程!

老奶奶看了都会的WSL2-Ubuntu连接USB设备教程!作者:SkyXZ CSDN:SkyXZ~-CSDN博客 博客园:SkyXZ - 博客园参考资料:微软官方文档连接 USB 设备 | Microsoft Learn在Win11上用WSL2安装Ubuntu来开发简直不要太爽!!!但是很多小伙伴会发现,欸~为什么我在宿主机上插入的USB设…

HighReport报表工具V4.0带来十大核心优势变化

1.概述经过一年时间产品升级研发,HighReport报表工具正式推出V4.0版本,报表算法和报表功能获得全面提升。HighReportV4.0带来全面质的飞跃,具有明显的产品优势。 2.亮点一:双父格扩展模型报表引擎核心算法是父子格扩展模型,下面是常见模型一般报表厂商下面的扩展模型是不支…

一个超经典 WinForm,WPF 卡死问题的终极反思

一:背景 1. 讲故事 写这篇文章起源于训练营里一位朋友最近在微信聊到他对这个问题使用了一种非常切实可行,简单粗暴的方式,并且也成功解决了公司里几个这样的卡死dump,如今在公司已是灵魂级人物,让我也尝到了什么叫反哺!对,这个东西叫 Harmony, github网址: https://gi…

nginx 简单实践:静态资源部署、URL 重写【nginx 实践系列之一】

本文为 nginx 简单实践系列文章之一,主要简单实践了两个内容:静态资源部署、重写,仅供参考。〇、前言 本文为 nginx 简单实践系列文章之一,主要简单实践了两个内容:静态资源部署、重写,仅供参考。 关于 Nginx 基础,以及安装和配置详解,可以参考博主过往文章: https://…

题解:AT_abc353_f [ABC353F] Tile Distance

[ABC353F] Tile Distance 题解 cnblogs 题目传送门:洛谷,Atcoder Solution 很恶心人的分类讨论题。 很显然走大格子大概率比走小格子快。 对终点和起点向上下左右枚举大格子,我们就把问题转化为给两个大格子 \((a,b)\)、\((c,d)\),求怎样走最快。 对角的大格子可以通过 \(2…

数字化转型中的项目管理优化:协作工具的优势与应用

一、企业数字化转型的背景与挑战 1.1 数字化转型的驱动力数字化转型是指企业通过采用数字技术、创新流程和业务模式,提升运营效率、创造新价值并优化客户体验。随着云计算、大数据、人工智能和物联网等技术的不断发展,数字化转型已成为企业实现长期竞争力和持续增长的重要战略…

rk3568屏幕抖动问题

问题描述:有时候操作屏幕界面,发现屏幕有抖动的情况。经跟RK原厂沟通,此问题跟给ddr供电的vdd_logic有关系。vdd_logic默认定义:vdd_logic: DCDC_REG1 {regulator-always-on;regulator-boot-on;regulator-min-microvolt = <500000>;regulator-max-microvolt = <13…