管理

C# 花费两分钟掌握委托与事件:从概念到实践

Posted on 2025-10-15 19:58  lzhdim  阅读(48)  评论(0)    收藏  举报

在 C# 编程中,委托和事件是实现回调机制和发布-订阅模式的核心技术。它们让对象之间能够进行松耦合的通信,是构建可扩展应用程序的重要工具。

1. 委托(Delegate)

1.1 委托的基本概念

委托是一种类型安全的函数指针,它定义了方法的签名,可以引用任何与其签名匹配的方法。

// 声明一个委托public delegate void MessageHandler(string message);
// 使用委托public class DelegateExample{    public static void ShowMessage(string msg)    {        Console.WriteLine($"信息: {msg}");    }
    public static void LogMessage(string msg)    {        Console.WriteLine($"[日志] {DateTime.Now}: {msg}");    }
    public static void Demo()    {        // 创建委托实例        MessageHandler handler = ShowMessage;
        // 调用委托        handler("Hello World!");
        // 多播委托 - 组合多个方法        handler += LogMessage;        handler("这是一个测试消息");    }}

1.2 实际应用场景:排序算法

public class SortExample{    // 定义比较委托    public delegate int CompareDelegate<T>(T x, T y);
    // 使用委托的冒泡排序    public static void BubbleSort<T>(T[] array, CompareDelegate<T> comparer)    {        for (int i = 0; i < array.Length - 1; i++)        {            for (int j = 0; j < array.Length - i - 1; j++)            {                if (comparer(array[j], array[j + 1]) > 0)                {                    // 交换元素                    T temp = array[j];                    array[j] = array[j + 1];                    array[j + 1] = temp;                }            }        }    }
    // 比较方法    public static int CompareInt(int x, int y)    {        return x.CompareTo(y);    }
    public static int CompareString(string x, string y)    {        return string.Compare(x, y);    }
    public static void Demo()    {        // 整数排序        int[] numbers = { 5, 2, 8, 1, 9 };        BubbleSort(numbers, CompareInt);        Console.WriteLine("排序后的数字: " + string.Join(", ", numbers));
        // 字符串排序        string[] names = { "John", "Alice", "Bob", "Charlie" };        BubbleSort(names, CompareString);        Console.WriteLine("排序后的名字: " + string.Join(", ", names));    }}

2. 事件(Event)

2.1 事件的基本概念

事件是基于委托的特殊构造,提供了更好的封装性和安全性。事件遵循发布-订阅模式。

// 事件发布者public class Button{    // 1. 定义事件委托    public delegate void ClickEventHandler(object sender, EventArgs e);
    // 2. 声明事件    public event ClickEventHandler Click;
    // 3. 触发事件的方法    protected virtual void OnClick(EventArgs e)    {        Click?.Invoke(this, e);    }
    public void Press()    {        Console.WriteLine("按钮被按下...");        OnClick(EventArgs.Empty);    }}
// 事件订阅者public class UserInterface{    public void SubscribeToButton(Button button)    {        // 订阅事件        button.Click += OnButtonClick;    }
    private void OnButtonClick(object sender, EventArgs e)    {        Console.WriteLine("UI: 按钮点击事件被处理");    }}

2.2 实际应用:温度监控系统

// 自定义事件参数public class TemperatureChangedEventArgs : EventArgs{    public double OldTemperature { get; }    public double NewTemperature { get; }    public DateTime ChangeTime { get; }
    public TemperatureChangedEventArgs(double oldTemp, double newTemp)    {        OldTemperature = oldTemp;        NewTemperature = newTemp;        ChangeTime = DateTime.Now;    }}
// 温度传感器(事件发布者)public class TemperatureSensor{    private double _currentTemperature;
    // 使用 EventHandler<T> 简化事件声明    public event EventHandler<TemperatureChangedEventArgs> TemperatureChanged;
    public double CurrentTemperature    {        get => _currentTemperature;        set        {            if (value != _currentTemperature)            {                double oldTemp = _currentTemperature;                _currentTemperature = value;                OnTemperatureChanged(oldTemp, value);            }        }    }
    protected virtual void OnTemperatureChanged(double oldTemp, double newTemp)    {        TemperatureChanged?.Invoke(this,             new TemperatureChangedEventArgs(oldTemp, newTemp));    }}
// 显示器(事件订阅者)public class Display{    public void SubscribeToSensor(TemperatureSensor sensor)    {        sensor.TemperatureChanged += OnTemperatureChanged;    }
    private void OnTemperatureChanged(object sender, TemperatureChangedEventArgs e)    {        Console.WriteLine($"显示器: 温度从 {e.OldTemperature}°C 变为 {e.NewTemperature}°C");        Console.WriteLine($"变化时间: {e.ChangeTime:HH:mm:ss}");
        if (e.NewTemperature > 30)        {            Console.WriteLine("警告: 温度过高!");        }        else if (e.NewTemperature < 10)        {            Console.WriteLine("警告: 温度过低!");        }    }}
// 数据记录器(另一个订阅者)public class DataLogger{    private readonly List<string> _log = new List<string>();
    public void SubscribeToSensor(TemperatureSensor sensor)    {        sensor.TemperatureChanged += OnTemperatureChanged;    }
    private void OnTemperatureChanged(object sender, TemperatureChangedEventArgs e)    {        string logEntry = $"{e.ChangeTime:yyyy-MM-dd HH:mm:ss} - " +                         $"温度变化: {e.OldTemperature} -> {e.NewTemperature}°C";        _log.Add(logEntry);        Console.WriteLine($"数据记录: {logEntry}");    }
    public void PrintLog()    {        Console.WriteLine("\n=== 温度变化记录 ===");        foreach (var entry in _log)        {            Console.WriteLine(entry);        }    }}

2.3 完整演示程序

class Program{    static void Main(string[] args)    {        Console.WriteLine("=== 委托演示 ===");        DelegateExample.Demo();
        Console.WriteLine("\n=== 排序演示 ===");        SortExample.Demo();
        Console.WriteLine("\n=== 温度监控系统演示 ===");
        // 创建温度传感器        var sensor = new TemperatureSensor();
        // 创建订阅者        var display = new Display();        var logger = new DataLogger();
        // 订阅事件        display.SubscribeToSensor(sensor);        logger.SubscribeToSensor(sensor);
        // 模拟温度变化        sensor.CurrentTemperature = 25.0;        sensor.CurrentTemperature = 28.5;        sensor.CurrentTemperature = 32.0;  // 触发高温警告        sensor.CurrentTemperature = 15.0;        sensor.CurrentTemperature = 8.5;   // 触发低温警告
        // 显示记录        logger.PrintLog();
        Console.WriteLine("\n=== 按钮事件演示 ===");        var button = new Button();        var ui = new UserInterface();
        ui.SubscribeToButton(button);        button.Press();
        Console.ReadLine();    }}

3. 委托和事件的高级用法

3.1 使用 Action 和 Func 委托

C# 提供了内置的泛型委托,可以避免自定义委托声明。

public class AdvancedDelegateExample{    public static void ProcessData(int[] data, Action<int> processAction)    {        foreach (var item in data)        {            processAction(item);        }    }
    public static List<TResult> TransformData<T, TResult>(        List<T> data, Func<T, TResult> transformFunc)    {        var result = new List<TResult>();        foreach (var item in data)        {            result.Add(transformFunc(item));        }        return result;    }
    public static void Demo()    {        // 使用 Action 委托        int[] numbers = { 1, 2, 3, 4, 5 };        ProcessData(numbers, x => Console.WriteLine($"处理数字: {x}"));
        // 使用 Func 委托        var names = new List<string> { "apple", "banana", "cherry" };        var upperNames = TransformData(names, name => name.ToUpper());
        Console.WriteLine("转换后的名字: " + string.Join(", ", upperNames));    }}

3.2 事件访问器

public class EventAccessorExample{    private EventHandler _myEvent;
    public event EventHandler MyEvent    {        add        {            Console.WriteLine("添加事件处理器");            _myEvent += value;        }        remove        {            Console.WriteLine("移除事件处理器");            _myEvent -= value;        }    }
    protected virtual void OnMyEvent()    {        _myEvent?.Invoke(this, EventArgs.Empty);    }}

4. 最佳实践和注意事项

  1. 命名约定:事件处理委托以 EventHandler 结尾,事件参数以 EventArgs 结尾
  2. 线程安全的事件调用

protected virtual void OnTemperatureChanged(double oldTemp, double newTemp){    var handler = TemperatureChanged;    if (handler != null)    {        handler(this, new TemperatureChangedEventArgs(oldTemp, newTemp));    }}

  1. 及时取消订阅防止内存泄漏:

public void UnsubscribeFromSensor(TemperatureSensor sensor){    sensor.TemperatureChanged -= OnTemperatureChanged;}

总结

委托和事件是 C# 中强大的特性,它们:

  • 委托:提供类型安全的回调机制,支持多播
  • 事件:基于委托,提供更好的封装性和安全性
  • 应用场景:GUI 编程、异步编程、插件系统、观察者模式等
  • 优势:松耦合、可扩展、易于测试

通过合理使用委托和事件,可以构建出灵活、可维护的应用程序架构。

 

Copyright © 2000-2022 Lzhdim Technology Software All Rights Reserved