在 C# 的世界里,委托(Delegate) 与 事件(Event) 是两个看似简单却深藏玄机的核心概念。
答案来了:
✅ 委托是“方法的容器”,事件是“封装的委托”,两者结合可构建强大的事件驱动架构!
✅ 代码实战+生产案例,手把手教你从“函数指针”到“事件风暴”!
✅ 性能优化+设计原则,告别“硬编码”陷阱!
// 定义委托类型(方法签名模板)
public delegate void MyDelegate(string message);
// 使用委托
public class Program
{
// 委托绑定的目标方法
public static void PrintMessage(string message)
{
Console.WriteLine($"委托调用:{message}");
}
public static void Main()
{
// 创建委托实例并绑定方法
MyDelegate myDelegate = new MyDelegate(PrintMessage);
// 或简化写法:MyDelegate myDelegate = PrintMessage;
// 调用委托
myDelegate("Hello Delegate!");
// 输出:委托调用:Hello Delegate!
}
}
代码注释:
delegate
:定义一个方法签名的“模具”,后续可绑定符合该签名的具体方法。MyDelegate myDelegate = PrintMessage;
:隐式转换语法糖,等价于显式构造。myDelegate("Hello Delegate!");
:调用委托时,实际调用的是绑定的方法。public delegate void MultiDelegate(int value);
public class MultiCastDemo
{
public static void Method1(int value) => Console.WriteLine($"Method1: {value}");
public static void Method2(int value) => Console.WriteLine($"Method2: {value}");
public static void MethodWithError(int value) => throw new Exception("模拟异常");
public static void Run()
{
MultiDelegate multiDelegate = Method1;
multiDelegate += Method2;
multiDelegate += MethodWithError; // 添加异常方法
try
{
// 调用多播委托(按绑定顺序执行)
multiDelegate(42);
}
catch (Exception ex)
{
// 异常会中断后续方法执行
Console.WriteLine($"捕获异常:{ex.Message}");
}
}
}
// 输出:
// Method1: 42
// Method2: 42
// 捕获异常:模拟异常
代码注释:
+=
:多播委托的链式绑定,支持多个方法的调用。public class LoginEventArgs : EventArgs
{
public string Username { get; set; }
}
public class UserAuthenticator
{
// 声明事件(只读,外部无法直接调用)
public event EventHandler<LoginEventArgs> LoginSuccess;
public void Login(string username, string password)
{
if (username == "admin" && password == "123")
{
// 安全触发事件(空检查+空合并操作符)
LoginSuccess?.Invoke(this, new LoginEventArgs { Username = username });
}
}
}
// 订阅与使用
public class Program
{
public static void Main()
{
var authenticator = new UserAuthenticator();
authenticator.LoginSuccess += OnLoginSuccess;
authenticator.Login("admin", "123");
}
private static void OnLoginSuccess(object sender, LoginEventArgs e)
{
Console.WriteLine($"用户 {e.Username} 登录成功!");
}
}
代码注释:
event
:将委托封装为事件,限制外部直接调用 Invoke()
。+=
和 -=
:外部只能通过订阅/取消订阅操作事件。sender
和 e
:标准事件参数模式,sender
表示触发事件的对象,e
包含事件数据。区别点 | 委托(Delegate) | 事件(Event) |
---|---|---|
触发权限 | 任何持有委托引用的代码均可调用 Invoke() 。 |
只有声明事件的类内部可以触发。 |
空引用风险 | 需手动检查 null 。 |
自动处理(event?.Invoke() 语法糖)。 |
封装性 | 低(外部可修改委托链)。 | 高(外部只能通过 += 和 -= 操作)。 |
典型应用 | 回调、动态方法调用、LINQ 查询。 | GUI 事件、观察者模式、模块解耦通信。 |
设计原则:
[ApiController]
[Route("[controller]")]
public class OrderController : ControllerBase
{
[HttpPost("PlaceOrder")]
public ActionResult<string> PlaceOrder([FromBody] OrderDTO order)
{
var request = HttpContext.Request;
// 1. 获取客户端 IP
string ipAddress = request.HttpContext.Connection.RemoteIpAddress?.ToString()
?? "Unknown";
// 2. 解析 User-Agent
string userAgent = request.Headers["User-Agent"].ToString();
var uaParser = UAParser.Parser.GetDefault();
var client = uaParser.Parse(userAgent);
string deviceType = client.Device.Family; // 如 "Mobile", "Desktop"
// 3. 记录日志到数据库(伪代码)
LogToDatabase(new LogEntry
{
OrderId = order.Id,
ClientIp = ipAddress,
DeviceType = deviceType,
UserAgent = userAgent,
OrderTime = DateTime.Now
});
return Ok("订单提交成功!");
}
private void LogToDatabase(LogEntry entry)
{
// 实际开发中需使用 ORM(如 Entity Framework)或 ADO.NET
// 示例:插入到 SQL Server
// using (var context = new AppDbContext())
// {
// context.Logs.Add(entry);
// context.SaveChanges();
// }
}
}
代码注释:
UAParser
:第三方库(需安装 UAParser
NuGet 包)用于解析 User-Agent。LogToDatabase
:实际开发中需使用 ORM 或 ADO.NET 实现数据库操作。OrderDTO
:订单数据模型(需自行定义)。Func
和 Action
// Func:无参数,有返回值
Func<int> getRandomNumber = () => new Random().Next(1, 100);
Console.WriteLine($"随机数:{getRandomNumber()}");
// Action:有参数,无返回值
Action<string> printMessage = message => Console.WriteLine($"Action调用:{message}");
printMessage("Hello Action!");
// Func:多参数,有返回值
Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine($"加法结果:{add(3, 5)}");
代码注释:
Func
:适用于需要返回值的回调场景(如计算、验证)。Action
:适用于无返回值的操作(如日志、通知)。public class DataProcessor
{
public event EventHandler<DataProcessedEventArgs> DataProcessed;
public void ProcessData(string data)
{
// 模拟异步处理
Task.Run(() =>
{
Thread.Sleep(1000); // 模拟耗时操作
var args = new DataProcessedEventArgs { Result = data.ToUpper() };
DataProcessed?.Invoke(this, args);
});
}
}
public class DataProcessedEventArgs : EventArgs
{
public string Result { get; set; }
}
// 使用事件
public class Program
{
public static void Main()
{
var processor = new DataProcessor();
processor.DataProcessed += OnDataProcessed;
processor.ProcessData("hello");
Console.WriteLine("继续执行其他任务...");
}
private static void OnDataProcessed(object sender, DataProcessedEventArgs e)
{
Console.WriteLine($"数据处理完成:{e.Result}");
}
}
代码注释:
Task.Run
:将事件处理异步化,避免阻塞主线程。DataProcessedEventArgs
:自定义事件参数,传递处理结果。原因:多播委托中任意方法抛出异常,后续方法将不再执行。
解决方案:
GetInvocationList()
遍历调用。foreach (var del in multiDelegate.GetInvocationList())
{
try
{
del.DynamicInvoke(42);
}
catch (Exception ex)
{
Console.WriteLine($"方法异常:{ex.Message}");
}
}
原因:事件订阅未及时取消,导致发布者无法被 GC 回收。
解决方案:
-=
。WeakReference
或第三方库(如 EventAggregator
)。墨工的终极建议:
别再用“硬编码”方式处理业务逻辑了!
Func
/Action
!event
+ EventHandler
瞬间搞定!