跳转到内容

C Sharp 委托与事件

来自代码酷

C#委托与事件[编辑 | 编辑源代码]

介绍[编辑 | 编辑源代码]

委托(Delegate)事件(Event)是C#中实现回调机制和发布-订阅模式的核心概念。委托是一种类型安全的函数指针,允许将方法作为参数传递或存储;事件是基于委托的封装,提供了一种安全的方式来实现观察者模式。理解这两个概念对于开发响应式、松耦合的应用程序至关重要。

核心概念[编辑 | 编辑源代码]

  • 委托:定义方法签名,可以指向任何匹配签名的方法。
  • 事件:是委托的封装,限制外部直接操作,仅允许添加/移除订阅者。

委托详解[编辑 | 编辑源代码]

委托声明与使用[编辑 | 编辑源代码]

委托通过delegate关键字声明,指定方法的返回类型和参数列表。

// 声明一个委托类型
public delegate void PrintMessage(string message);

class Program
{
    // 匹配委托签名的方法
    static void DisplayMessage(string text)
    {
        Console.WriteLine($"Message: {text}");
    }

    static void Main()
    {
        // 实例化委托并绑定方法
        PrintMessage printer = DisplayMessage;
        printer("Hello, Delegate!"); // 调用委托
    }
}

输出:

Message: Hello, Delegate!

多播委托[编辑 | 编辑源代码]

委托支持多播(组合多个方法),通过+=-=操作符管理调用列表。

PrintMessage multiPrinter = DisplayMessage;
multiPrinter += (string msg) => Console.WriteLine($"Upper: {msg.ToUpper()}");
multiPrinter("Multi-cast");

输出:

Message: Multi-cast
Upper: MULTI-CAST

事件详解[编辑 | 编辑源代码]

事件是委托的封装,仅允许在声明类内触发,外部只能订阅/取消订阅。

标准事件模式[编辑 | 编辑源代码]

使用EventHandler委托和继承自EventArgs的参数类。

public class TemperatureChangedEventArgs : EventArgs
{
    public double NewTemperature { get; set; }
}

public class Thermostat
{
    // 1. 定义事件
    public event EventHandler<TemperatureChangedEventArgs> TemperatureChanged;

    // 2. 触发事件的方法
    protected virtual void OnTemperatureChanged(double temp)
    {
        TemperatureChanged?.Invoke(this, new TemperatureChangedEventArgs { NewTemperature = temp });
    }

    public void SimulateChange(double newTemp)
    {
        OnTemperatureChanged(newTemp);
    }
}

class Program
{
    static void Main()
    {
        Thermostat thermostat = new Thermostat();
        // 3. 订阅事件
        thermostat.TemperatureChanged += (sender, e) => 
            Console.WriteLine($"Temperature now: {e.NewTemperature}°C");
        
        thermostat.SimulateChange(23.5);
    }
}

输出:

Temperature now: 23.5°C

自定义委托事件[编辑 | 编辑源代码]

不使用EventHandler模板时:

public delegate void AlertHandler(string message);

public class SecuritySystem
{
    public event AlertHandler IntrusionDetected;

    public void DetectMotion()
    {
        IntrusionDetected?.Invoke("Motion detected at " + DateTime.Now);
    }
}

实际应用案例[编辑 | 编辑源代码]

UI开发中的按钮点击[编辑 | 编辑源代码]

在WPF或WinForms中,按钮点击通过事件处理:

button.Click += (sender, e) => 
{
    MessageBox.Show("Button clicked!");
};

观察者模式实现[编辑 | 编辑源代码]

classDiagram class Subject { +Attach(Observer o) +Detach(Observer o) +Notify() } class ConcreteSubject { -state +GetState() +SetState() } class Observer { <<interface>> +Update() } class ConcreteObserver { +Update() } Subject <|-- ConcreteSubject Observer <|-- ConcreteObserver ConcreteSubject o-- Observer

高级主题[编辑 | 编辑源代码]

协变与逆变(C# 4.0+)[编辑 | 编辑源代码]

委托支持泛型类型参数的变体:

  • 协变(out):允许返回派生程度更大的类型
  • 逆变(in):允许参数接受派生程度更小的类型

解析失败 (语法错误): {\displaystyle \text{协变:} \ \ \ \ Func<Animal> \rightarrow Func<Dog> \\ \text{逆变:} \ \ \ \ Action<Dog> \rightarrow Action<Animal> }

异步事件[编辑 | 编辑源代码]

使用async/await处理耗时事件:

thermostat.TemperatureChanged += async (sender, e) => 
{
    await Task.Delay(1000);
    Console.WriteLine($"Async handling: {e.NewTemperature}");
};

常见问题[编辑 | 编辑源代码]

问题 解决方案
事件触发为null 使用?.Invoke()空值检查
内存泄漏 及时取消订阅(-=
多线程竞争 缓存委托引用后调用

总结[编辑 | 编辑源代码]

  • 委托是类型安全的回调机制
  • 事件是受限的委托,用于实现发布-订阅
  • 标准模式使用EventHandler<TEventArgs>
  • 实际应用于UI交互、观察者模式等场景