多播委托

 

引言

在 C# 中,委托(Delegate)是一种类型安全的函数指针,允许将方法作为参数传递。多播委托(Multicast Delegate)则是可以调用多个方法的委托实例。本文将详细介绍多播委托的概念、使用场景以及如何在实际开发中应用多播委托。

什么是委托?

委托的基本概念

委托是 C# 中的一种类型,它封装了对具有特定参数列表和返回类型的静态或实例方法的引用。通过委托,你可以将方法作为参数传递给其他方法,从而实现回调机制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<code-pre class="code-pre" id="pre-DDDthY"><code-line class="line-numbers-rows"></code-line>// 定义一个委托类型
<code-line class="line-numbers-rows"></code-line>public delegate void MyDelegate(string message);
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>// 定义两个符合委托签名的方法
<code-line class="line-numbers-rows"></code-line>public void Method1(string message)
<code-line class="line-numbers-rows"></code-line>{
<code-line class="line-numbers-rows"></code-line>    Console.WriteLine("Method1: " + message);
<code-line class="line-numbers-rows"></code-line>}
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>public void Method2(string message)
<code-line class="line-numbers-rows"></code-line>{
<code-line class="line-numbers-rows"></code-line>    Console.WriteLine("Method2: " + message);
<code-line class="line-numbers-rows"></code-line>}
</code-pre>

 

创建和使用委托实例

1
2
3
4
5
6
7
8
9
10
11
12
<code-pre class="code-pre" id="pre-aWTYwx"><code-line class="line-numbers-rows"></code-line>public class Program
<code-line class="line-numbers-rows"></code-line>{
<code-line class="line-numbers-rows"></code-line>    public static void Main()
<code-line class="line-numbers-rows"></code-line>    {
<code-line class="line-numbers-rows"></code-line>        // 创建委托实例并绑定方法
<code-line class="line-numbers-rows"></code-line>        MyDelegate del = new MyDelegate(Method1);
<code-line class="line-numbers-rows"></code-line>       
<code-line class="line-numbers-rows"></code-line>        // 调用委托
<code-line class="line-numbers-rows"></code-line>        del("Hello, World!");
<code-line class="line-numbers-rows"></code-line>    }
<code-line class="line-numbers-rows"></code-line>}
</code-pre>

  

什么是多播委托?

多播委托的概念

多播委托是指可以调用多个方法的委托实例。当调用多播委托时,它会依次调用所有绑定的方法。C# 中的所有委托都是多播委托,即使它们只绑定了一个方法。

创建多播委托

可以通过 + 操作符将多个方法绑定到同一个委托实例上,形成多播委托。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<code-pre class="code-pre" id="pre-6hfPJJ"><code-line class="line-numbers-rows"></code-line>public class Program
<code-line class="line-numbers-rows"></code-line>{
<code-line class="line-numbers-rows"></code-line>    public static void Main()
<code-line class="line-numbers-rows"></code-line>    {
<code-line class="line-numbers-rows"></code-line>        // 创建委托实例并绑定第一个方法
<code-line class="line-numbers-rows"></code-line>        MyDelegate del = new MyDelegate(Method1);
<code-line class="line-numbers-rows"></code-line>       
<code-line class="line-numbers-rows"></code-line>        // 使用 + 操作符添加第二个方法
<code-line class="line-numbers-rows"></code-line>        del += new MyDelegate(Method2);
<code-line class="line-numbers-rows"></code-line>       
<code-line class="line-numbers-rows"></code-line>        // 调用多播委托
<code-line class="line-numbers-rows"></code-line>        del("Hello, World!");
<code-line class="line-numbers-rows"></code-line>    }
<code-line class="line-numbers-rows"></code-line>}</code-pre>

输出结果

1
2
3
<code-pre class="code-pre" id="pre-PrhMhb"><code-line class="line-numbers-rows"></code-line>Method1: Hello, World!
<code-line class="line-numbers-rows"></code-line>Method2: Hello, World!
</code-pre>

  

 

多播委托的特性

方法调用顺序

多播委托中的方法按它们被添加的顺序依次调用。如果需要改变调用顺序,可以通过重新组合委托实例来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<code-pre class="code-pre" id="pre-7zRPSy"><code-line class="line-numbers-rows"></code-line>public class Program
<code-line class="line-numbers-rows"></code-line>{
<code-line class="line-numbers-rows"></code-line>    public static void Main()
<code-line class="line-numbers-rows"></code-line>    {
<code-line class="line-numbers-rows"></code-line>        MyDelegate del1 = new MyDelegate(Method1);
<code-line class="line-numbers-rows"></code-line>        MyDelegate del2 = new MyDelegate(Method2);
<code-line class="line-numbers-rows"></code-line>       
<code-line class="line-numbers-rows"></code-line>        // 组合委托实例
<code-line class="line-numbers-rows"></code-line>        MyDelegate combinedDel = del2 + del1;
<code-line class="line-numbers-rows"></code-line>       
<code-line class="line-numbers-rows"></code-line>        // 调用组合后的委托
<code-line class="line-numbers-rows"></code-line>        combinedDel("Hello, World!");
<code-line class="line-numbers-rows"></code-line>    }
<code-line class="line-numbers-rows"></code-line>}</code-pre>

移除方法

可以通过 - 操作符从多播委托中移除某个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<code-pre class="code-pre" id="pre-rkAZ2M"><code-line class="line-numbers-rows"></code-line>public class Program
<code-line class="line-numbers-rows"></code-line>{
<code-line class="line-numbers-rows"></code-line>    public static void Main()
<code-line class="line-numbers-rows"></code-line>    {
<code-line class="line-numbers-rows"></code-line>        MyDelegate del = new MyDelegate(Method1) + new MyDelegate(Method2);
<code-line class="line-numbers-rows"></code-line>       
<code-line class="line-numbers-rows"></code-line>        // 移除 Method1
<code-line class="line-numbers-rows"></code-line>        del -= new MyDelegate(Method1);
<code-line class="line-numbers-rows"></code-line>       
<code-line class="line-numbers-rows"></code-line>        // 调用委托
<code-line class="line-numbers-rows"></code-line>        del("Hello, World!");
<code-line class="line-numbers-rows"></code-line>    }
<code-line class="line-numbers-rows"></code-line>}
</code-pre>

 

空委托检查

在调用多播委托之前,最好检查其是否为 null,以避免空引用异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<code-pre class="code-pre" id="pre-Kbz87s"><code-line class="line-numbers-rows"></code-line>public class Program
<code-line class="line-numbers-rows"></code-line>{
<code-line class="line-numbers-rows"></code-line>    public static void Main()
<code-line class="line-numbers-rows"></code-line>    {
<code-line class="line-numbers-rows"></code-line>        MyDelegate del = null;
<code-line class="line-numbers-rows"></code-line>       
<code-line class="line-numbers-rows"></code-line>        // 检查委托是否为 null
<code-line class="line-numbers-rows"></code-line>        if (del != null)
<code-line class="line-numbers-rows"></code-line>        {
<code-line class="line-numbers-rows"></code-line>            del("Hello, World!");
<code-line class="line-numbers-rows"></code-line>        }
<code-line class="line-numbers-rows"></code-line>        else
<code-line class="line-numbers-rows"></code-line>        {
<code-line class="line-numbers-rows"></code-line>            Console.WriteLine("No methods to invoke.");
<code-line class="line-numbers-rows"></code-line>        }
<code-line class="line-numbers-rows"></code-line>    }
<code-line class="line-numbers-rows"></code-line>}</code-pre>

多播委托的应用场景

事件处理

多播委托最常见的应用场景之一是事件处理。C# 的事件机制基于多播委托,允许多个订阅者响应同一事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<code-pre class="code-pre" id="pre-xQC62D"><code-line class="line-numbers-rows"></code-line>using System;
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>public class Publisher
<code-line class="line-numbers-rows"></code-line>{
<code-line class="line-numbers-rows"></code-line>    // 定义事件
<code-line class="line-numbers-rows"></code-line>    public event EventHandler<MyEventArgs> MyEvent;
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>    // 触发事件的方法
<code-line class="line-numbers-rows"></code-line>    public void TriggerEvent()
<code-line class="line-numbers-rows"></code-line>    {
<code-line class="line-numbers-rows"></code-line>        OnMyEvent(new MyEventArgs { Message = "Event Triggered" });
<code-line class="line-numbers-rows"></code-line>    }
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>    // 保护方法用于触发事件
<code-line class="line-numbers-rows"></code-line>    protected virtual void OnMyEvent(MyEventArgs e)
<code-line class="line-numbers-rows"></code-line>    {
<code-line class="line-numbers-rows"></code-line>        MyEvent?.Invoke(this, e);
<code-line class="line-numbers-rows"></code-line>    }
<code-line class="line-numbers-rows"></code-line>}
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>public class Subscriber
<code-line class="line-numbers-rows"></code-line>{
<code-line class="line-numbers-rows"></code-line>    public void HandleEvent(object sender, MyEventArgs e)
<code-line class="line-numbers-rows"></code-line>    {
<code-line class="line-numbers-rows"></code-line>        Console.WriteLine($"Subscriber received: {e.Message}");
<code-line class="line-numbers-rows"></code-line>    }
<code-line class="line-numbers-rows"></code-line>}
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>public class MyEventArgs : EventArgs
<code-line class="line-numbers-rows"></code-line>{
<code-line class="line-numbers-rows"></code-line>    public string Message { get; set; }
<code-line class="line-numbers-rows"></code-line>}
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>public class Program
<code-line class="line-numbers-rows"></code-line>{
<code-line class="line-numbers-rows"></code-line>    public static void Main()
<code-line class="line-numbers-rows"></code-line>    {
<code-line class="line-numbers-rows"></code-line>        var publisher = new Publisher();
<code-line class="line-numbers-rows"></code-line>        var subscriber1 = new Subscriber();
<code-line class="line-numbers-rows"></code-line>        var subscriber2 = new Subscriber();
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>        // 订阅事件
<code-line class="line-numbers-rows"></code-line>        publisher.MyEvent += subscriber1.HandleEvent;
<code-line class="line-numbers-rows"></code-line>        publisher.MyEvent += subscriber2.HandleEvent;
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>        // 触发事件
<code-line class="line-numbers-rows"></code-line>        publisher.TriggerEvent();
<code-line class="line-numbers-rows"></code-line>    }
<code-line class="line-numbers-rows"></code-line>}
</code-pre>

  

 

日志记录

多播委托也可以用于日志记录系统,允许多个日志记录器同时处理日志消息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<code-pre class="code-pre" id="pre-8PmPRt"><code-line class="line-numbers-rows"></code-line>public delegate void Logger(string message);
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>public class FileLogger
<code-line class="line-numbers-rows"></code-line>{
<code-line class="line-numbers-rows"></code-line>    public void LogToFile(string message)
<code-line class="line-numbers-rows"></code-line>    {
<code-line class="line-numbers-rows"></code-line>        Console.WriteLine($"Logged to file: {message}");
<code-line class="line-numbers-rows"></code-line>    }
<code-line class="line-numbers-rows"></code-line>}
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>public class ConsoleLogger
<code-line class="line-numbers-rows"></code-line>{
<code-line class="line-numbers-rows"></code-line>    public void LogToConsole(string message)
<code-line class="line-numbers-rows"></code-line>    {
<code-line class="line-numbers-rows"></code-line>        Console.WriteLine($"Logged to console: {message}");
<code-line class="line-numbers-rows"></code-line>    }
<code-line class="line-numbers-rows"></code-line>}
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>public class Program
<code-line class="line-numbers-rows"></code-line>{
<code-line class="line-numbers-rows"></code-line>    public static void Main()
<code-line class="line-numbers-rows"></code-line>    {
<code-line class="line-numbers-rows"></code-line>        Logger logger = new Logger(new FileLogger().LogToFile);
<code-line class="line-numbers-rows"></code-line>        logger += new Logger(new ConsoleLogger().LogToConsole);
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>        logger("This is a log message.");
<code-line class="line-numbers-rows"></code-line>    }
<code-line class="line-numbers-rows"></code-line>}
</code-pre>

  

所有委托都是 MulticastDelegate 吗?

在 C# 中,所有委托类型实际上都继承自 MulticastDelegate 。这意味着从技术上讲,所有的委托实例都可以被视为多播委托(Multicast Delegate),即使它们只绑定了一个方法。然而,这并不意味着每个委托实例都会实际绑定多个方法。

1. 委托的底层实现

C# 的委托是基于 System.MulticastDelegate 类实现的。MulticastDelegate 继承自 System.Delegate,并添加了对多播支持的功能。因此,任何通过 delegate 关键字定义的委托类型,其实例都可以包含多个方法调用列表(即调用链)。

1
2
<code-pre class="code-pre" id="pre-6GbmG2"><code-line class="line-numbers-rows"></code-line>public delegate void MyDelegate(string message);
</code-pre>

  

上述代码定义了一个名为 MyDelegate 的委托类型,它实际上继承自 MulticastDelegate

2. 单播委托 vs 多播委托

  • 单播委托:当一个委托实例只绑定一个方法时,我们通常称之为单播委托。虽然它是 MulticastDelegate 的实例,但它的调用链中只有一个方法。
1
<code-pre class="code-pre" id="pre-bymfiJ"><code-line class="line-numbers-rows"></code-line>MyDelegate del = new MyDelegate(Method1);</code-pre>
  • 多播委托:当一个委托实例绑定多个方法时,我们称之为多播委托。这些方法会按顺序依次调用。
1
<code-pre class="code-pre" id="pre-Npe6Hk"><code-line class="line-numbers-rows"></code-line>MyDelegate del = new MyDelegate(Method1) + new MyDelegate(Method2);</code-pre>

3. 为什么说所有委托都是 MulticastDelegate

从类层次结构的角度来看,所有委托类型都继承自 MulticastDelegate,因此它们具备多播的能力。但这并不意味着每个委托实例都会实际使用这一能力。你可以选择性地将多个方法绑定到一个委托实例上,也可以只绑定一个方法。

4. 验证委托类型

你可以通过反射来验证这一点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<code-pre class="code-pre" id="pre-fZnrsa"><code-line class="line-numbers-rows"></code-line>using System;
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>public class Program
<code-line class="line-numbers-rows"></code-line>{
<code-line class="line-numbers-rows"></code-line>    public static void Main()
<code-line class="line-numbers-rows"></code-line>    {
<code-line class="line-numbers-rows"></code-line>        // 定义一个委托类型
<code-line class="line-numbers-rows"></code-line>        public delegate void MyDelegate(string message);
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>        // 创建委托实例
<code-line class="line-numbers-rows"></code-line>        MyDelegate del = new MyDelegate(Method1);
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>        // 检查委托类型
<code-line class="line-numbers-rows"></code-line>        Console.WriteLine(del.GetType().BaseType);  // 输出: System.MulticastDelegate
<code-line class="line-numbers-rows"></code-line>    }
<code-line class="line-numbers-rows"></code-line>
<code-line class="line-numbers-rows"></code-line>    public static void Method1(string message)
<code-line class="line-numbers-rows"></code-line>    {
<code-line class="line-numbers-rows"></code-line>        Console.WriteLine("Method1: " + message);
<code-line class="line-numbers-rows"></code-line>    }
<code-line class="line-numbers-rows"></code-line>}
</code-pre>

  

输出结果:

1
2
<code-pre class="code-pre" id="pre-BjxXkc"><code-line class="line-numbers-rows"></code-line>System.MulticastDelegate
</code-pre>

  

5. 总结

  • 所有委托类型都继承自 MulticastDelegate,因此理论上所有委托实例都可以作为多播委托使用。
  • 实际使用中,委托实例可以是单播或多播,取决于你是否将多个方法绑定到同一个委托实例上。
  • 灵活性:这种设计提供了极大的灵活性,允许你在需要时轻松扩展委托的功能,而无需改变委托类型的定义。

总结

多播委托是 C# 中一种强大的功能,允许你将多个方法绑定到同一个委托实例上,并按顺序调用这些方法。它们广泛应用于事件处理、日志记录等场景,简化了代码逻辑并提高了灵活性。通过本文的介绍,希望你能更好地理解和应用多播委托,提升你的 C# 编程技能。

 


__EOF__

本文作者one996
本文链接https://www.cnblogs.com/one966/p/18637558.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   一半春风  阅读(146)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 上周热点回顾(2.17-2.23)
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
点击右上角即可分享
微信分享提示