委托和事件是C#中实现松耦合和响应式编程的核心机制,本文将全面深入这两个重要概念。
委托实质上是一个类型安全的函数指针,它定义了方法的签名:
// 声明委托类型
public delegate int MathOperation(int a, int b);
class Program
{
static int Add(int x, int y) => x + y;
static int Multiply(int x, int y) => x * y;
static void Main()
{
// 创建委托实例
MathOperation operation = Add;
// 调用委托
int result = operation(3, 5); // 8
// 更改委托引用
operation = Multiply;
result = operation(3, 5); // 15
// 多播委托
operation += Add;
result = operation(3, 5); // 只返回最后一个方法的结果(Multiply)
}
}
.NET提供了内置泛型委托,避免自己声明:
// Func - 无参有返回值
Func<string> getTime = () => DateTime.Now.ToString();
// Func - 多参有返回值
Func<int, int, int> addFunc = (x, y) => x + y;
// Action - 无返回值
Action<string> logAction = msg => Console.WriteLine(msg);
// Predicate - 返回bool
Predicate<int> isEven = num => num % 2 == 0;
// 协变(out) - 返回类型可以是派生类
delegate TResult Func<out TResult>();
Func<string> stringFunc = () => "Hello";
Func<object> objectFunc = stringFunc; // 合法
// 逆变(in) - 参数类型可以是基类
delegate void Action<in T>(T arg);
Action<object> objectAction = obj => Console.WriteLine(obj);
Action<string> stringAction = objectAction; // 合法
事件是基于委托的发布-订阅模型:
public class TemperatureMonitor
{
// 1. 定义委托类型
public delegate void TemperatureChangedHandler(decimal newTemperature);
// 2. 声明事件
public event TemperatureChangedHandler TemperatureChanged;
private decimal _currentTemp;
public decimal CurrentTemperature
{
get => _currentTemp;
set
{
if (_currentTemp != value)
{
_currentTemp = value;
// 3. 触发事件
OnTemperatureChanged(_currentTemp);
}
}
}
// 4. 受保护的虚方法触发事件(标准模式)
protected virtual void OnTemperatureChanged(decimal newTemp)
{
TemperatureChanged?.Invoke(newTemp);
}
}
// 使用
var monitor = new TemperatureMonitor();
monitor.TemperatureChanged += temp =>
Console.WriteLine($"温度变化: {temp}°C");
monitor.CurrentTemperature = 23.5m; // 触发事件
自定义事件订阅逻辑:
public class EventSource
{
private EventHandler _myEvent;
public event EventHandler MyEvent
{
add
{
Console.WriteLine($"添加订阅者: {value.Method.Name}");
_myEvent += value;
}
remove
{
Console.WriteLine($"移除订阅者: {value.Method.Name}");
_myEvent -= value;
}
}
public void RaiseEvent() => _myEvent?.Invoke(this, EventArgs.Empty);
}
Func<int, int, int> longRunningOp = (a, b) => {
Thread.Sleep(2000);
return a + b;
};
// BeginInvoke/EndInvoke模式(传统异步)
IAsyncResult result = longRunningOp.BeginInvoke(3, 5, null, null);
int sum = longRunningOp.EndInvoke(result); // 阻塞直到完成
// Task封装(现代方式)
Task<int> task = Task.Run(() => longRunningOp(3, 5));
task.ContinueWith(t => Console.WriteLine($"结果为: {t.Result}"));
解决内存泄漏问题:
// 使用WeakEventManager
public class NewsPublisher
{
public event EventHandler<NewsEventArgs> NewsPublished;
public void Publish(string headline)
{
NewsPublished?.Invoke(this, new NewsEventArgs(headline));
}
}
public class NewsEventArgs : EventArgs
{
public string Headline { get; }
public NewsEventArgs(string headline) => Headline = headline;
}
// 订阅方(可能会被GC回收)
public class NewsSubscriber
{
public NewsSubscriber(NewsPublisher publisher)
{
WeakEventManager<NewsPublisher, NewsEventArgs>
.AddHandler(publisher, "NewsPublished", OnNewsPublished);
}
private void OnNewsPublished(object sender, NewsEventArgs e)
{
Console.WriteLine($"收到新闻: {e.Headline}");
}
}
LINQ重度依赖委托:
List<Product> products = GetProducts();
// Where接受Predicate委托
var cheapProducts = products.Where(p => p.Price < 50);
// Select使用Func委托
var names = products.Select(p => p.Name);
// 自定义委托链
Func<Product, bool> filter = p => p.Price > 20;
filter += p => p.Stock > 0;
// 应用多条件筛选
var filtered = products.Where(p => filter.GetInvocationList()
.All(f => ((Func<Product,bool>)f)(p)));
// 使用事件实现
public class Subject
{
public event EventHandler StateChanged;
private string _state;
public string State
{
get => _state;
set
{
_state = value;
StateChanged?.Invoke(this, EventArgs.Empty);
}
}
}
public class Observer
{
public Observer(Subject subject)
{
subject.StateChanged += OnSubjectStateChanged;
}
private void OnSubjectStateChanged(object sender, EventArgs e)
{
Console.WriteLine($"主题状态已更改为: {((Subject)sender).State}");
}
}
public interface ICommand
{
void Execute();
void Undo();
}
public class DelegateCommand : ICommand
{
private readonly Action _execute;
private readonly Action _undo;
public DelegateCommand(Action execute, Action undo)
{
_execute = execute;
_undo = undo;
}
public void Execute() => _execute();
public void Undo() => _undo();
}
// 使用
var command = new DelegateCommand(
execute: () => Console.WriteLine("执行操作"),
undo: () => Console.WriteLine("撤销操作")
);
command.Execute();
缓存委托实例:避免重复创建相同委托
private static readonly Func<int, int, int> AddFunc = (a, b) => a + b;
避免装箱:值类型参数的委托调用会导致装箱
// 不好:会有装箱
Action<object> logObject = obj => Console.WriteLine(obj);
logObject(42);
// 更好:使用泛型避免装箱
Action<int> logInt = num => Console.WriteLine(num);
logInt(42);
遵循标准模式:
public event EventHandler<MyEventArgs> MyEvent;
protected virtual void OnMyEvent(MyEventArgs e)
{
MyEvent?.Invoke(this, e);
}
线程安全调用:
EventHandler temp = MyEvent;
if (temp != null)
{
temp(this, EventArgs.Empty);
}
命名规范:
[Name]EventHandler
[Name]EventArgs
On[EventName]
public void ProcessData()
{
// 委托方式
Func<int, int> square = x => x * x;
// 本地函数方式(性能更好)
int SquareLocal(int x) => x * x;
// 两者都可作为参数传递
Calculate(square);
Calculate(SquareLocal);
}
void Calculate(Func<int, int> operation)
{
Console.WriteLine(operation(5));
}
public class Timer
{
public event Action<string> Tick;
public void Start()
{
// 使用lambda直接处理事件
System.Threading.Timer timer = new(_ =>
{
Tick?.Invoke($"Tick at {DateTime.Now:T}");
}, null, 0, 1000);
}
}
通过深入理解委托和事件,开发者可以构建松耦合、响应式的应用程序架构。这些特性特别适用于GUI编程、插件系统、异步编程等场景,是高级C#开发必备的核心技能。