.NET泛型EventHandler<TEventArgs>设计疑问:逆变与约束移除原因
EventHandler<TEventArgs> Design Great questions—these dig into some of the pragmatic design choices behind C#'s event model. Let's break each one down:
1. Why isn't TEventArgs marked as contravariant (in)?
First, a key context point: the generic EventHandler<TEventArgs> launched with C# 2, but generic contravariance (the in modifier) wasn't added to the language until C# 4. When the generic delegate was first designed, the syntax for variance didn't even exist.
Even after C# 4 introduced variance support, the .NET team chose not to retroactively add the in modifier to TEventArgs. Here's why:
- The event model is built around publishers emitting specific event data types, and subscribers handling those exact (or more general) types. In real-world code, contravariant conversions for event handlers are rarely needed—most developers subscribe with handlers that expect the precise data type the publisher sends.
- Adding contravariance could introduce subtle breaking changes for existing code, even if rare. The team prioritized backward compatibility over a feature with limited practical utility.
- The non-generic
EventHandler(which usesEventArgs) already acts as a fallback for handlers that don't need specific event data, reducing the need for contravariant conversions between generic handler types.
2. Why isn't there a generic constraint requiring TEventArgs to inherit from System.EventArgs?
Early drafts of the generic EventHandler<TEventArgs> did include a where TEventArgs : EventArgs constraint, but the .NET design team removed it before release. Their reasoning centers on flexibility:
- The constraint offered no real practical value. The non-generic
EventHandlerenforcesEventArgsfor context-free event data, but the generic version was meant to let developers pass any type of data with an event—whether that's a customEventArgssubclass, a primitive likeintorstring, or a plain data object. - Requiring inheritance from
EventArgswould force developers to create unnecessary wrapper classes for simple event data. For example, if you want to pass a string message with an event, you shouldn't have to define aStringEventArgs : EventArgsclass just to comply with a constraint. - The event model doesn't actually depend on
EventArgsunder the hood—it's just a convention from the non-generic era. The generic delegate was designed to break free from that convention when it makes sense.
内容的提问来源于stack exchange,提问作者Zack ISSOIR

