Though I can't find a documented limitation in the C# spec, I can see at least two problems with supporting such an event in C#/CLR, both related to how it is raised.
First difficulty: in the language
C# only allows raising an event from within the type that declares it. But if your generic class doesn't even know the number of parameters it T
, what should the code that raises the event look like?
class MyClass<T> where T: Delegate
{
public event T MyEvent;
public void DoSomething()
{
// raise MyEvent here
MyEvent(/* what goes here? */);
}
}
Of course, you can make MyClass
abstract and say that inheritors that specify the type of T
would raise the event. However, this would be quite an inconsistent language design, to my opinion.
Second difficulty: in the compiler
CLR implements runtime generics. This means, that compiler must generate IL that should be good at runtime for any T
that meets the generic constraints.
Raising an event is basically invoking a delegate that's stored in the event field. The compiler should generate IL that roughly includes these steps:
- push delegate object reference onto the stack
- push argument 1
- push argument 2
- ....
- push argument N
- call delegate's Invoke method
If the delegate isn't void
, an additional step is required:
- pop return value from the stack and possibly store it in a field or a local variable
As you can see, the generated IL strictly depends on the number of arguments and whether the delegate is void
. Therefore, such IL cannot be good for any Delegate
.
In contrast
Having event delegate with generic parameters is perfectly OK, such as:
delegate void MyEventHandler<K, V>(K key, V value);
because the number of the parameters and whether the delegate is void
is known at compile time. In this case the same set of IL instructions can be generated that is good for any K
and V
. In the IL, K
and V
are generated as type placeholders, which CLR is capable of resolving at runtime.