Description
I had a situation where I was trying to delegate an event handler to another one with a more derived argument. This is possible now in .NET 10 because EventHandler is contravariant. This compiles but I discovered that it can throw if you register handlers of different types on different events (see repro).
It looks like the MulticastDelegate being used under the covers is doing a strict type check so it doesn't allow contravariance in this specific case. In the first two lines I can see in the decompiled IL that a EventHandler<Bar> is constructed but in the third line a EventHandler<Foo> is constructed.
Reproduction Steps
// This is a LINQPad script
void Main()
{
Handler += BarHandler; // this works
Handler += FooHandler; // this works
DelegatingHandler += FooHandler; // this throws. works if I comment out the lines above.
Handler?.Invoke(null, new Bar());
}
// You can define other methods, fields, classes and namespaces here
event EventHandler<Bar> Handler;
event EventHandler<Foo> DelegatingHandler
{
add => Handler += value;
remove => Handler -= value;
}
void BarHandler(object sender, Bar arg)
{
arg.Dump();
}
void FooHandler(object sender, Foo arg)
{
arg.Dump();
}
class Foo
{
}
class Bar : Foo
{
}
Expected behavior
Doesn't throw.
Actual behavior
Throws an ArgumentException:
Delegates must be of the same type.
at System.MulticastDelegate.CombineImpl(Delegate follow)
at UserQuery.add_Handler(EventHandler`1 value)
at UserQuery.add_DelegatingHandler(EventHandler`1 value), line 16
at UserQuery.Main(), line 5
Regression?
No response
Known Workarounds
No response
Configuration
.NET 10 on Windows 11
Other information
No response
Description
I had a situation where I was trying to delegate an event handler to another one with a more derived argument. This is possible now in .NET 10 because EventHandler is contravariant. This compiles but I discovered that it can throw if you register handlers of different types on different events (see repro).
It looks like the MulticastDelegate being used under the covers is doing a strict type check so it doesn't allow contravariance in this specific case. In the first two lines I can see in the decompiled IL that a
EventHandler<Bar>is constructed but in the third line aEventHandler<Foo>is constructed.Reproduction Steps
Expected behavior
Doesn't throw.
Actual behavior
Throws an ArgumentException:
Regression?
No response
Known Workarounds
No response
Configuration
.NET 10 on Windows 11
Other information
No response