1

According to MSDN documentation delegates in event handlers support contravariance, for example you can use one event handler with EventArgs as its generic parameter of EventHandler<T> for different event that has other parameter:

static class Program
{
    static void Main()
    {
       var a = new A();
        a.event1 += a_event1;
        a.event2 += a_event1;
    }

    static void a_event1(object sender, EventArgs e)
    {}
}

public class A
{
    public event EventHandler<EventArgs>        event1;
    public event EventHandler<EventArgsDerived> event2;
}

public class EventArgsDerived : EventArgs
{}

I noticed that EventHandler definition is :

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e); 

and generic TEventArgs parameter is defined without using an in Keyword.

So how contravariance is supported and how compiler accepts base class parameter of type EventArgs for event handler?

Naser Asadi
  • 1,153
  • 18
  • 35

2 Answers2

1

Contravariance is implicit through the use of EventArgs base class in standard event handling pattern and TEventArgs is only used as an input parameter.

The variance that you mean (using in and out) is for type conversion, for exapmle:

var a = new EventHandler<EventArgs>((o, e) => { return; });
var b = new EventHandler<ErrorEventArgs>((o, e) => { return; });
b = a;

Gives you a compile error because TEventArgs is not specified as in. But the following is legal:

var x = new Action<EventArgs>((e) => { return; });
var y = new Action<ErrorEventArgs>((e) => { return; });
y = x;
brz
  • 5,926
  • 1
  • 18
  • 18
  • It gives compile error because it violates constructor argument. If you use method name in assignment as in my example it will compile. – Naser Asadi Sep 02 '14 at 09:27
  • Then why doesn't it generate an error on second case ? Look at it this way, in your case event1 expects an object as it's first argument and an EventArgs as it's second, so it doesn't care if you pass it an ErrorEventArgs or anything else that derives from EventArgs. All it needs is EventArgs functionality. – brz Sep 02 '14 at 12:50
  • It's exactly contravariance issue in delegates and needs **in** keyword. In your case `Action` is defined as `public delegate void Action(T obj);` and has the keyword. – Naser Asadi Sep 02 '14 at 12:56
  • Yes, and that's why I can't cast an EventHandler to EventHandler BUT I can use an EventHander delegate INSTEAD OF EventHandler delegate. – brz Sep 02 '14 at 13:47
  • This is allowed if generic parameter is used with **in** keyword like 'Action '. This keyword is not present in 'EventHandler' which is my question. – Naser Asadi Sep 02 '14 at 14:21
1

You are confusing generic covariance/contravariance with method group conversion variance.

As you observe, EventHandler<TEventArgs> does not support generic contravariance in its type argument due to the lack of the in keyword. This type of variance was added in C# 4.0.

But you can still assign handlers that take less derived types in their argument list thanks to method group conversion variance. This is an older type of variance support, that was added in C# 2.0. You can read more about it in Eric Lipperts blog post on the subject.

Søren Boisen
  • 1,669
  • 22
  • 41