位置: 文档库 > C#(.NET) > C#委托与事件讲解

C#委托与事件讲解

周星驰 上传于 2020-03-26 10:23

C#委托与事件讲解》

在C#编程中,委托(Delegate)和事件(Event)是两个核心概念,它们为面向对象编程提供了强大的灵活性,尤其在实现观察者模式、解耦组件以及处理异步操作时发挥了关键作用。本文将从基础概念出发,结合实际案例,深入解析委托与事件的原理、用法及其在.NET框架中的典型应用场景。

一、委托(Delegate)的基础概念

委托是一种类型安全的函数指针,它允许将方法作为参数传递给其他方法。委托的核心价值在于它能够将方法调用封装为对象,从而实现方法的动态调用和组合。

1. 委托的定义与声明

委托的声明类似于方法签名,但需要使用delegate关键字。例如,定义一个接受两个整数参数并返回整数的委托:

public delegate int MathOperation(int x, int y);

此委托可以指向任何符合签名的方法,例如加法或乘法方法:

public int Add(int a, int b) => a + b;
public int Multiply(int a, int b) => a * b;

2. 委托的实例化与调用

委托实例化时需要传入匹配的方法:

MathOperation operation = Add; // 指向Add方法
int result = operation(3, 4);  // 调用Add方法,结果为7

委托也可以指向多个方法,形成多播委托(Multicast Delegate):

MathOperation operations = Add;
operations += Multiply; // 添加第二个方法
operations(2, 3);       // 依次调用Add和Multiply,但结果仅返回最后一个方法的结果

注意:多播委托的返回值是最后一个方法的返回值,若需收集所有结果,需手动遍历委托的GetInvocationList()方法。

3. 泛型委托与Action/Func

C#提供了内置的泛型委托ActionFunc,简化了委托的声明:

  • Action:无返回值的方法委托。
  • Func:有返回值的方法委托。
Action logMessage = message => Console.WriteLine(message);
Func add = (x, y) => x + y;

二、事件(Event)的原理与应用

事件是基于委托的一种机制,用于实现发布-订阅模式。事件允许对象在状态变化时通知其他对象,而无需直接引用这些对象。

1. 事件的基本结构

事件需要包含一个委托类型的字段和一个事件声明。例如,定义一个通知事件:

public class Notifier
{
    // 定义委托
    public delegate void NotificationHandler(string message);
    
    // 声明事件
    public event NotificationHandler OnNotification;
    
    public void RaiseEvent()
    {
        OnNotification?.Invoke("Hello, Event!"); // 触发事件
    }
}

订阅者可以这样注册事件:

Notifier notifier = new Notifier();
notifier.OnNotification += message => Console.WriteLine($"Received: {message}");
notifier.RaiseEvent();

2. 事件的安全性与封装

事件通过封装委托字段,限制外部代码只能+=-=订阅/取消订阅,而不能直接调用或修改委托。这种封装提高了代码的安全性。

3. 标准事件模式

.NET框架推荐使用标准事件模式,包括:

  • 事件参数继承自EventArgs
  • 事件命名以On开头(如OnChanged)。

示例:

public class TemperatureMonitor
{
    public class TemperatureChangedEventArgs : EventArgs
    {
        public double NewTemperature { get; }
        public TemperatureChangedEventArgs(double temp) => NewTemperature = temp;
    }
    
    public event EventHandler TemperatureChanged;
    
    public void UpdateTemperature(double newTemp)
    {
        TemperatureChanged?.Invoke(this, new TemperatureChangedEventArgs(newTemp));
    }
}

订阅者可以这样处理事件:

var monitor = new TemperatureMonitor();
monitor.TemperatureChanged += (sender, e) => 
    Console.WriteLine($"Temperature changed to {e.NewTemperature}");

三、委托与事件的典型应用场景

1. 异步编程与回调

委托常用于异步操作的回调。例如,模拟一个耗时任务:

public class AsyncTask
{
    public void Execute(Action callback)
    {
        Task.Run(() => 
        {
            Thread.Sleep(1000); // 模拟耗时操作
            callback("Task completed!");
        });
    }
}

// 使用
var task = new AsyncTask();
task.Execute(result => Console.WriteLine(result));

2. UI事件处理

在WinForms或WPF中,按钮点击等UI操作通过事件通知:

// WinForms示例
Button button = new Button();
button.Click += (sender, e) => MessageBox.Show("Button clicked!");

3. 观察者模式实现

事件是观察者模式的天然实现。例如,股票价格变动通知:

public class Stock
{
    public event Action PriceChanged;
    private double _price;
    
    public double Price
    {
        get => _price;
        set
        {
            _price = value;
            PriceChanged?.Invoke(_price);
        }
    }
}

// 订阅者
var stock = new Stock();
stock.PriceChanged += price => Console.WriteLine($"New price: {price}");
stock.Price = 100.5;

四、高级主题:委托与事件的性能与最佳实践

1. 性能考虑

多播委托的调用会遍历所有订阅方法,若订阅者过多可能影响性能。在高频场景中,需谨慎使用事件。

2. 避免内存泄漏

事件订阅会导致订阅者对象无法被垃圾回收(除非显式取消订阅)。长期存在的发布者(如静态类)可能引发内存泄漏。解决方案:

  • 实现IDisposable并在Dispose中取消订阅。
  • 使用弱引用事件(如WeakEventManager)。

3. 线程安全

多线程环境下触发事件需加锁,避免竞争条件:

private object _lock = new object();
private EventHandler _event;

public event EventHandler Event
{
    add { lock (_lock) { _event += value; } }
    remove { lock (_lock) { _event -= value; } }
}

private void RaiseEvent()
{
    var handler = _event;
    if (handler != null)
    {
        lock (_lock) { handler(this, EventArgs.Empty); }
    }
}

五、委托与事件的对比总结

特性 委托 事件
用途 封装方法调用 基于委托的通知机制
访问修饰符 通常为public 封装委托字段,仅暴露+=/-=
典型场景 回调、方法组合 发布-订阅、UI交互

关键词:C#委托、C#事件多播委托Action委托Func委托观察者模式事件参数、线程安全、内存泄漏

简介:本文详细讲解了C#中委托与事件的概念、声明方式、多播委托、泛型委托(Action/Func)以及事件的标准模式。通过代码示例展示了委托在回调和异步编程中的应用,以及事件在UI交互和观察者模式中的实现。同时分析了性能优化、内存管理和线程安全等高级主题,帮助开发者全面掌握委托与事件的核心机制。