多播委托
引言
在 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__
本文链接:https://www.cnblogs.com/one966/p/18637558.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 上周热点回顾(2.17-2.23)
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章