当前位置: 首页 > news >正文

C# 委托和事件详解,委托 vs 方法封装解析

C# 委托和事件详解

    • 1. 委托 (Delegate)
      • 1.1 委托的基本概念
      • 1.2 委托的使用方式
      • 1.3 内置委托类型
    • 2. 事件 (Event)
      • 2.1 事件的基本概念
      • 2.2 使用 EventHandler 泛型委托
    • 3. 实际开发中的应用场景
      • 3.1 UI 事件处理 (WinForms/WPF)
      • 3.2 异步操作和回调
      • 3.3 插件系统和扩展性
      • 3.4 消息总线和事件驱动架构
      • 3.5 自定义控件和组件
      • 3.6 数据验证和业务规则
    • 4. 最佳实践和注意事项
      • 4.1 事件模式最佳实践
      • 4.2 使用弱事件模式避免内存泄漏
    • 5. 总结
  • C# 委托 vs 方法封装:详细解析
    • 1. 基本概念区别
      • 方法封装 (Method Encapsulation)
      • 委托 (Delegate)
    • 2. 为什么委托有时更好?
      • 2.1 更高的灵活性 - 运行时方法选择
      • 2.2 更好的解耦 - 事件系统
      • 2.3 支持函数式编程范式
      • 2.4 支持异步和回调模式
      • 2.5 LINQ和集合操作的强大功能
    • 3. 实际应用场景对比
      • 场景:排序算法
    • 4. 总结对比
    • 5. 何时选择委托?

1. 委托 (Delegate)

1.1 委托的基本概念

委托是一种类型安全的函数指针,它定义了方法的签名,可以引用具有相同签名的方法。

// 委托声明
public delegate void MessageHandler(string message);
public delegate int MathOperation(int a, int b);
public delegate bool FilterCondition<T>(T item);

1.2 委托的使用方式

// 基本委托使用
public class DelegateExamples
{// 声明委托public delegate void DisplayMessage(string text);public delegate int Calculate(int x, int y);public void DemoBasicDelegate(){// 实例化委托DisplayMessage messageDelegate = ShowMessage;Calculate calcDelegate = Add;// 调用委托messageDelegate("Hello, Delegate!");int result = calcDelegate(10, 5);Console.WriteLine($"Result: {result}");// 多播委托DisplayMessage multiDelegate = ShowMessage;multiDelegate += ShowWarning;multiDelegate("This will be shown twice!");}private void ShowMessage(string text) => Console.WriteLine($"INFO: {text}");private void ShowWarning(string text) => Console.WriteLine($"WARNING: {text}");private int Add(int a, int b) => a + b;
}

1.3 内置委托类型

public class BuiltInDelegates
{public void DemoBuiltInDelegates(){// Action - 无返回值的方法Action<string> actionExample = Console.WriteLine;actionExample("Action delegate");// Func - 有返回值的方法Func<int, int, int> funcExample = (a, b) => a + b;int sum = funcExample(5, 3);// Predicate - 返回bool的方法Predicate<string> predicateExample = s => s.Length > 5;bool isLong = predicateExample("Hello World");}
}

2. 事件 (Event)

2.1 事件的基本概念

事件是基于委托的特殊构造,提供了一种发布-订阅模式,用于实现观察者模式。

// 事件声明
public class EventPublisher
{// 1. 定义委托public delegate void SomethingHappenedHandler(object sender, EventArgs e);// 2. 声明事件public event SomethingHappenedHandler SomethingHappened;// 3. 触发事件的方法protected virtual void OnSomethingHappened(){SomethingHappened?.Invoke(this, EventArgs.Empty);}public void DoWork(){// 执行某些工作...OnSomethingHappened(); // 触发事件}
}public class EventSubscriber
{public void Subscribe(EventPublisher publisher){publisher.SomethingHappened += Publisher_SomethingHappened;}private void Publisher_SomethingHappened(object sender, EventArgs e){Console.WriteLine("Event received!");}
}

2.2 使用 EventHandler 泛型委托

public class TemperatureMonitor
{// 使用标准的EventHandler<T>模式public event EventHandler<TemperatureChangedEventArgs> TemperatureChanged;private double _currentTemperature;public double CurrentTemperature{get => _currentTemperature;set{if (_currentTemperature != value){_currentTemperature = value;OnTemperatureChanged(new TemperatureChangedEventArgs(value));}}}protected virtual void OnTemperatureChanged(TemperatureChangedEventArgs e){TemperatureChanged?.Invoke(this, e);}
}// 自定义事件参数
public class TemperatureChangedEventArgs : EventArgs
{public double Temperature { get; }public TemperatureChangedEventArgs(double temperature){Temperature = temperature;}
}

3. 实际开发中的应用场景

3.1 UI 事件处理 (WinForms/WPF)

// WinForms 示例
public partial class MainForm : Form
{private Button _submitButton;private TextBox _nameTextBox;public MainForm(){InitializeComponents();// 订阅事件_submitButton.Click += SubmitButton_Click;_nameTextBox.TextChanged += NameTextBox_TextChanged;}private void SubmitButton_Click(object sender, EventArgs e){string name = _nameTextBox.Text;MessageBox.Show($"Hello, {name}!");}private void NameTextBox_TextChanged(object sender, EventArgs e){_submitButton.Enabled = !string.IsNullOrEmpty(_nameTextBox.Text);}
}

3.2 异步操作和回调

public class FileDownloader
{public event EventHandler<DownloadProgressEventArgs> ProgressChanged;public event EventHandler<DownloadCompletedEventArgs> DownloadCompleted;public event EventHandler<DownloadErrorEventArgs> DownloadError;public async Task DownloadFileAsync(string url, string savePath){try{using (var client = new HttpClient()){client.Timeout = TimeSpan.FromMinutes(30);using (var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))using (var stream = await response.Content.ReadAsStreamAsync())using (var fileStream = new FileStream(savePath, FileMode.Create)){var totalBytes = response.Content.Headers.ContentLength ?? -1L;var buffer = new byte[8192];var totalRead = 0L;int read;while ((read = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0){await fileStream.WriteAsync(buffer, 0, read);totalRead += read;// 报告进度ProgressChanged?.Invoke(this, new DownloadProgressEventArgs(totalRead, totalBytes));}}}// 下载完成DownloadCompleted?.Invoke(this, new DownloadCompletedEventArgs(savePath));}catch (Exception ex){DownloadError?.Invoke(this, new DownloadErrorEventArgs(ex.Message));}}
}

3.3 插件系统和扩展性

// 插件系统架构
public interface IPlugin
{string Name { get; }void Initialize();void Execute();
}public class PluginHost
{public event EventHandler<PluginLoadedEventArgs> PluginLoaded;public event EventHandler<PluginExecutedEventArgs> PluginExecuted;private List<IPlugin> _plugins = new List<IPlugin>();public void LoadPlugin(IPlugin plugin){_plugins.Add(plugin);plugin.Initialize();PluginLoaded?.Invoke(this, new PluginLoadedEventArgs(plugin.Name));}public void ExecuteAllPlugins(){foreach (var plugin in _plugins){plugin.Execute();PluginExecuted?.Invoke(this, new PluginExecutedEventArgs(plugin.Name));}}
}

3.4 消息总线和事件驱动架构

public class MessageBus
{private readonly Dictionary<Type, List<Delegate>> _handlers = new Dictionary<Type, List<Delegate>>();public void Subscribe<TMessage>(Action<TMessage> handler){var messageType = typeof(TMessage);if (!_handlers.ContainsKey(messageType)){_handlers[messageType] = new List<Delegate>();}_handlers[messageType].Add(handler);}public void Publish<TMessage>(TMessage message){var messageType = typeof(TMessage);if (_handlers.TryGetValue(messageType, out var handlers)){foreach (var handler in handlers){if (handler is Action<TMessage> action){action(message);}}}}
}// 使用消息总线
public class OrderService
{private readonly MessageBus _messageBus;public OrderService(MessageBus messageBus){_messageBus = messageBus;}public void PlaceOrder(Order order){// 处理订单逻辑...// 发布订单创建事件_messageBus.Publish(new OrderCreatedEvent(order));}
}public class EmailService
{public EmailService(MessageBus messageBus){messageBus.Subscribe<OrderCreatedEvent>(SendOrderConfirmation);}private void SendOrderConfirmation(OrderCreatedEvent orderEvent){// 发送确认邮件Console.WriteLine($"Sending confirmation email for order {orderEvent.Order.Id}");}
}

3.5 自定义控件和组件

public class CustomButton : Control
{public event EventHandler<CustomButtonClickEventArgs> CustomClick;protected override void OnMouseClick(MouseEventArgs e){base.OnMouseClick(e);var args = new CustomButtonClickEventArgs{ClickLocation = e.Location,Button = e.Button,Timestamp = DateTime.Now};CustomClick?.Invoke(this, args);}
}public class CustomButtonClickEventArgs : EventArgs
{public Point ClickLocation { get; set; }public MouseButtons Button { get; set; }public DateTime Timestamp { get; set; }
}

3.6 数据验证和业务规则

public class Validator
{public event EventHandler<ValidationErrorEventArgs> ValidationError;public bool Validate(object entity){var isValid = true;// 执行各种验证规则...if (entity is User user){if (string.IsNullOrEmpty(user.Email)){OnValidationError(new ValidationErrorEventArgs("Email is required", nameof(user.Email)));isValid = false;}if (user.Age < 18){OnValidationError(new ValidationErrorEventArgs("Must be at least 18 years old", nameof(user.Age)));isValid = false;}}return isValid;}protected virtual void OnValidationError(ValidationErrorEventArgs e){ValidationError?.Invoke(this, e);}
}

4. 最佳实践和注意事项

4.1 事件模式最佳实践

public class ProperEventImplementation
{// 使用EventHandler<T>而不是自定义委托public event EventHandler<CustomEventArgs> StandardEvent;// 使用protected virtual方法触发事件protected virtual void OnStandardEvent(CustomEventArgs e){StandardEvent?.Invoke(this, e);}// 线程安全的事件调用public event EventHandler<CustomEventArgs> ThreadSafeEvent;protected virtual void OnThreadSafeEvent(CustomEventArgs e){var handler = ThreadSafeEvent;handler?.Invoke(this, e);}// 避免内存泄漏 - 及时取消订阅public void SubscribeAndUnsubscribe(){var publisher = new EventPublisher();// 订阅publisher.SomethingHappened += HandleEvent;// 工作完成后取消订阅publisher.SomethingHappened -= HandleEvent;}private void HandleEvent(object sender, CustomEventArgs e){// 处理事件}
}

4.2 使用弱事件模式避免内存泄漏

public class WeakEventSource
{private readonly List<WeakReference<EventHandler<CustomEventArgs>>> _weakHandlers = new List<WeakReference<EventHandler<CustomEventArgs>>>();public event EventHandler<CustomEventArgs> WeakEvent{add => _weakHandlers.Add(new WeakReference<EventHandler<CustomEventArgs>>(value));remove => RemoveHandler(value);}protected virtual void OnWeakEvent(CustomEventArgs e){foreach (var weakRef in _weakHandlers.ToArray()){if (weakRef.TryGetTarget(out var handler)){handler?.Invoke(this, e);}else{_weakHandlers.Remove(weakRef);}}}private void RemoveHandler(EventHandler<CustomEventArgs> handler){// 实现移除逻辑}
}

5. 总结

委托的核心优势:

  • 方法作为参数传递
  • 回调机制
  • 运行时方法选择
  • 支持函数式编程

事件的核心优势:

  • 实现观察者模式
  • 组件间松耦合通信
  • 支持多订阅者
  • 内置线程安全机制

实际应用选择:

  • 使用委托当需要将方法作为参数传递或需要回调时
  • 使用事件当需要实现发布-订阅模式或组件间通信时
  • 对于UI交互、异步操作、插件系统等场景,事件是更好的选择
  • 对于算法策略、排序比较等场景,委托更合适

委托和事件是C#中实现灵活、可扩展架构的重要工具,正确使用可以大大提高代码的可维护性和可扩展性。


C# 委托 vs 方法封装:详细解析

1. 基本概念区别

方法封装 (Method Encapsulation)

// 传统方法封装 - 直接在类中定义方法
public class Calculator
{public int Add(int a, int b){return a + b;}public int Multiply(int a, int b){return a * b;}
}// 使用
var calc = new Calculator();
int result = calc.Add(5, 3); // 直接调用

委托 (Delegate)

// 委托定义
public delegate int MathOperation(int a, int b);public class Calculator
{public int Add(int a, int b) => a + b;public int Multiply(int a, int b) => a * b;
}// 使用委托
var calc = new Calculator();
MathOperation operation = calc.Add; // 将方法赋值给委托
int result = operation(5, 3); // 通过委托调用

2. 为什么委托有时更好?

2.1 更高的灵活性 - 运行时方法选择

public class PaymentProcessor
{// 传统方式 - 需要多个if/switch语句public void ProcessPayment(string paymentType, decimal amount){if (paymentType == "CreditCard")ProcessCreditCard(amount);else if (paymentType == "PayPal")ProcessPayPal(amount);else if (paymentType == "BankTransfer")ProcessBankTransfer(amount);}private void ProcessCreditCard(decimal amount) { /* 实现 */ }private void ProcessPayPal(decimal amount) { /* 实现 */ }private void ProcessBankTransfer(decimal amount) { /* 实现 */ }
}// 使用委托的更好方式
public delegate void PaymentHandler(decimal amount);public class FlexiblePaymentProcessor
{private Dictionary<string, PaymentHandler> _paymentHandlers;public FlexiblePaymentProcessor(){_paymentHandlers = new Dictionary<string, PaymentHandler>{["CreditCard"] = ProcessCreditCard,["PayPal"] = ProcessPayPal,["BankTransfer"] = ProcessBankTransfer};}public void ProcessPayment(string paymentType, decimal amount){if (_paymentHandlers.TryGetValue(paymentType, out var handler)){handler(amount); // 直接调用委托,无需条件判断}}private void ProcessCreditCard(decimal amount) { /* 实现 */ }private void ProcessPayPal(decimal amount) { /* 实现 */ }private void ProcessBankTransfer(decimal amount) { /* 实现 */ }// 可以动态添加新的支付方式public void RegisterHandler(string paymentType, PaymentHandler handler){_paymentHandlers[paymentType] = handler;}
}

2.2 更好的解耦 - 事件系统

// 传统紧耦合方式
public class Button
{private TextBox _textBox; // 直接依赖具体类public Button(TextBox textBox){_textBox = textBox;}public void Click(){_textBox.Clear(); // 直接调用具体方法}
}// 使用委托的解耦方式
public class Button
{public event Action OnClick; // 使用事件委托public void Click(){OnClick?.Invoke(); // 不关心具体实现}
}public class TextBox
{public void Clear() { /* 清空文本框 */ }
}// 使用
var button = new Button();
var textBox = new TextBox();button.OnClick += textBox.Clear; // 运行时绑定,完全解耦

2.3 支持函数式编程范式

// 传统面向对象方式
public class DataProcessor
{public List<int> FilterEvenNumbers(List<int> numbers){var result = new List<int>();foreach (var number in numbers){if (number % 2 == 0)result.Add(number);}return result;}public List<int> FilterPositiveNumbers(List<int> numbers){var result = new List<int>();foreach (var number in numbers){if (number > 0)result.Add(number);}return result;}// 每个过滤条件都需要一个新方法...
}// 使用委托的函数式方式
public class FunctionalDataProcessor
{public delegate bool FilterCondition(int number);public List<int> FilterNumbers(List<int> numbers, FilterCondition condition){return numbers.Where(n => condition(n)).ToList();}
}// 使用
var processor = new FunctionalDataProcessor();
var numbers = new List<int> { -2, -1, 0, 1, 2, 3, 4 };// 可以传递任何匹配委托签名的方法
var evenNumbers = processor.FilterNumbers(numbers, n => n % 2 == 0);
var positiveNumbers = processor.FilterNumbers(numbers, n => n > 0);// 甚至可以动态创建条件
FilterCondition customCondition = n => n > 0 && n % 2 == 0;
var result = processor.FilterNumbers(numbers, customCondition);

2.4 支持异步和回调模式

// 传统同步方式 - 阻塞调用
public class FileProcessor
{public void ProcessFile(string filePath){var content = File.ReadAllText(filePath); // 同步阻塞// 处理内容...}
}// 使用委托的异步回调方式
public class AsyncFileProcessor
{public delegate void FileProcessingCallback(string content);public void ProcessFileAsync(string filePath, FileProcessingCallback callback){Task.Run(() =>{var content = File.ReadAllText(filePath); // 在后台线程执行callback(content); // 完成后通过委托回调});}
}// 使用
var processor = new AsyncFileProcessor();
processor.ProcessFileAsync("test.txt", content =>
{Console.WriteLine($"文件内容: {content}");// UI线程可以继续响应,不会被阻塞
});

2.5 LINQ和集合操作的强大功能

public class Product
{public string Name { get; set; }public decimal Price { get; set; }public int CategoryId { get; set; }
}public class ProductService
{private List<Product> _products;// 传统方式 - 为每个查询写特定方法public List<Product> GetExpensiveProducts(){var result = new List<Product>();foreach (var product in _products){if (product.Price > 100)result.Add(product);}return result;}public List<Product> GetProductsByCategory(int categoryId){var result = new List<Product>();foreach (var product in _products){if (product.CategoryId == categoryId)result.Add(product);}return result;}// 使用委托和LINQ的现代方式public IEnumerable<Product> FilterProducts(Func<Product, bool> predicate){return _products.Where(predicate);}
}// 使用
var service = new ProductService();// 可以轻松组合各种查询条件
var expensiveProducts = service.FilterProducts(p => p.Price > 100);
var specificCategory = service.FilterProducts(p => p.CategoryId == 5);
var complexQuery = service.FilterProducts(p => p.Price > 50 && p.Name.Contains("Pro"));

3. 实际应用场景对比

场景:排序算法

// 传统方式 - 为每种排序标准写不同方法
public class Sorter
{public void SortByName(List<Person> people) { /* 实现 */ }public void SortByAge(List<Person> people) { /* 实现 */ }public void SortBySalary(List<Person> people) { /* 实现 */ }
}// 委托方式 - 一个方法处理所有排序
public class FlexibleSorter
{public delegate int Comparison<in T>(T x, T y);public void Sort<T>(List<T> list, Comparison<T> comparison){// 使用提供的比较委托进行排序list.Sort((x, y) => comparison(x, y));}
}// 使用
var people = new List<Person>();
var sorter = new FlexibleSorter();// 可以轻松切换排序标准
sorter.Sort(people, (p1, p2) => p1.Name.CompareTo(p2.Name));
sorter.Sort(people, (p1, p2) => p1.Age.CompareTo(p2.Age));
sorter.Sort(people, (p1, p2) => p1.Salary.CompareTo(p2.Salary));

4. 总结对比

特性方法封装委托
灵活性低 - 编译时绑定高 - 运行时绑定
解耦程度低 - 紧耦合高 - 松耦合
代码复用有限优秀
扩展性需要修改源码无需修改现有代码
函数式支持有限优秀
事件处理困难天然支持
异步编程需要额外机制天然支持

5. 何时选择委托?

  1. 需要运行时决定调用哪个方法时
  2. 实现事件系统和回调机制时
  3. 需要高度解耦的组件通信时
  4. 实现策略模式或模板方法模式时
  5. 进行函数式编程和LINQ操作时
  6. 需要异步编程和非阻塞调用时

委托提供了更强的表达能力和灵活性,特别是在需要动态行为、事件处理和函数式编程风格的场景中。然而,对于简单的、确定性的操作,直接方法调用可能更简单明了。


文章转载自:

http://dneOUJdb.mzcsp.cn
http://t7PZ50Xr.mzcsp.cn
http://L74iBPv5.mzcsp.cn
http://bkXVI8Gn.mzcsp.cn
http://2PM1g6nd.mzcsp.cn
http://O69AreHM.mzcsp.cn
http://md4M0u0e.mzcsp.cn
http://hbQ2BzLX.mzcsp.cn
http://OYzBpCBe.mzcsp.cn
http://ImqeANcx.mzcsp.cn
http://WNjrzHAf.mzcsp.cn
http://2wGCsC4A.mzcsp.cn
http://pfaHtaYB.mzcsp.cn
http://eQ86exyU.mzcsp.cn
http://CR1Q64Ec.mzcsp.cn
http://AznrNx92.mzcsp.cn
http://n1LKFu7j.mzcsp.cn
http://9wwhN0BP.mzcsp.cn
http://br4FgY3Q.mzcsp.cn
http://rNL1yGgw.mzcsp.cn
http://ru8pWSFl.mzcsp.cn
http://dJJ2r69V.mzcsp.cn
http://kNAem0EZ.mzcsp.cn
http://6LmC7kku.mzcsp.cn
http://b3iITfFm.mzcsp.cn
http://qsnULDyA.mzcsp.cn
http://f31hxTNb.mzcsp.cn
http://sphU1u3Y.mzcsp.cn
http://XJmE0WAX.mzcsp.cn
http://cpQqTCuu.mzcsp.cn
http://www.dtcms.com/a/388652.html

相关文章:

  • MariaDB源码编译安装
  • 多智能体编排之王:深度解析微软Semantic Kernel的AgentOrchestration架构革命
  • AI工具推荐之ezremove.ai
  • 关于Address Editor中修改基地址和地址空间的指南
  • 【Linux 系统探幽:从入门到内核・系统编程开篇】基础指令与权限精讲,筑牢系统开发根基
  • 【STL库】哈希封装 unordered_map/unordered_set
  • 【AI编程】Qoder AI 编程工具从部署到深度使用实战详解
  • 网络原理——数据链路层
  • 大语言模型的 “幻觉” 难题:技术成因、解决方案与应用风险规避
  • 状态保留功耗门控 SRPG (State Retention Power Gating)
  • Elman神经网络多输入多输出回归预测+SHAP可解释分析+新数据预测(MATLAB源码)
  • 408 王道数据结构的学习记录
  • 使用内存映射读取文件和写入文件,并进行性能测试
  • SQL的UNION用法大全介绍
  • 从Web原生到高性能:如何优化企业数据库管理工具
  • 基于python新能源汽车数据分析可视化系统 懂车帝 Scrapy爬虫 Django框架 Vue框架 大数据项目(源码+文档)✅
  • 线性回归和 softmax 回归
  • mysql远程访问连接设置
  • 《WINDOWS 环境下32位汇编语言程序设计》学习17章 PE文件(2)
  • Linux网络编程:从协议到实战
  • Vector 底层实现详解
  • OpenShift Virtualization - 虚机存储的相关概念 DataVolume、CDI 和 StorageProfile
  • 2025年Web自动化测试与Selenium面试题收集:从基础到进阶的全方位解析
  • pytorch中的FSDP
  • 贪心算法与材料切割问题详解
  • 2. 结构体
  • MySQL 核心操作:多表联合查询与数据库备份恢复
  • vue3学习日记(十四):两大API选型指南
  • 微信支付回调成功通知到本地
  • 量化交易 - Simple Regression 简单线性回归(机器学习)