I bookmarked this Question but answering much later, sorry about that.
I think you have learned about valid assignment between the instance of base and derived classes, and that created you confusion. The problem is: you applied some learned rules mechanically without attempt to understand why.
Consider assignment between you event argument classes:
void Test() {
UserActionEventArgs userAction = new UserActionEventArgs();
CompilationEventArgs complilationAction = new CompilationEventArgs();
userAction = complilationAction;
}
I added arrows and "base" and "derived" comment to make a picture "graphical".
All correct, assignment goes right-to-left, so base class can be only on left. Now, why?
Let's add some member in the derived class:
class CompilationEventArgs : UserActionEventArgs { int myField; }
If second assignment was possible, the instance of derived class would try to word with
myField
which does not exist (even worse to would be call non-existing method). This should not be allowed.
Now, the situation with adding of the event looks the opposite:
UserControl() {
UserActionRequested += UserActionHandler;
}
Well, this is because the assignment of the objects of event argument types in done in the direction opposite to the shown above, but again in the direction shown in first sample (which you seemingly understand).
First, consider how you fire the event:
void FireUserAction() {
if (UserActionRequested != null)
UserActionRequested(this, new UserActionEventArgs());
}
Imagine for a second that you could add your handle working with the derived type of event argument rather than the base type:
void CompilationHandler(object sender, CompilationEventArgs eventArgs) {
}
Imagine that this code is called somehow. It means assignment of the variable of derived type to the value of the base run-time type. The actual parameter of
eventArgs
would try to work with the field
myField
(because formal parameter is of the derived type where this member exists), but during run-time this member does not exists, because the formal (compile-time) parameter is of the base type, where this member does not exist. That's why the compiler will not allow adding this event handler in first place.
I hope this will resolve your confusion.
—SA