0

I could need some help to get this compile. I just want to pass a class type (SuccessEventArgs) as parameter for a generic class DebugEvent<TArgs> where TArgs : System.EventArgs.But for some reason this would not work ..

namespace MyInterface
{
    [Serializable]
    public class SuccessEventArgs : System.EventArgs
    {
        public SuccessEventArgs(string data);

        public byte[] GetData();
    }
}

public class DebugEvent<TArgs> where TArgs : System.EventArgs 
{
    // ...
}


// ///////////////////////////////////////////////////////////////////////


public abstract class DebugEventHandler
{
    protected DebugEvent<EventArgs> m_programmingSucceededEvent = null;
}

public class MyDebugEventHandler : DebugEventHandler
{
    override public void InitializeEventHandler(int programmingSuccessCode, int breakepointReachedCode)
    {
        m_programmingSucceededEvent = new DebugEvent<SuccessEventArgs>(ref m_eventSignal, programmingSuccessCode, this);
    }
}

The error message:

Cannot implicitly convert type 'DebugEvent<SuccessEventArgs>' to 'DebugEvent<System.EventArgs>'

Shouldn't that be possible?

Stefan Falk
  • 23,898
  • 50
  • 191
  • 378

3 Answers3

4

What you want is called covariance. Is has some limitations:

  • Only interfaces can use covariance, not classes
  • A generic type parameter can only be made covariant if it is not used in the input to any method. So it must only used in the return value of properties and methods.

You can define a covariant interface like this:

public interface IDebugEvent<out TArgs> where TArgs : System.EventArgs

But note this will not compile if you have a method which takes a TArgs-typed parameter. It's relatively straightforward to come up with examples why allowing this would break type-safety (see, for example, Jon Skeet's answer to this question). If this is a requirement for you, you will have to rethink your design.

In order to actually use this, you will have to be working with variables of the interface type, rather than the concrete class. So you can do:

IDebugEvent<EventArgs> m_programmingSucceededEvent = new DebugEvent<SuccessEventArgs>();

But not:

DebugEvent<EventArgs> m_programmingSucceededEvent = new DebugEvent<SuccessEventArgs>();
Community
  • 1
  • 1
Ben Aaronson
  • 6,955
  • 2
  • 23
  • 38
1

Add an interface IDebugEvent<out T> where T : EventArgs and have DebugEvent<T> : IDebugEvent<T> where T : EventArgs

Then the following will work:

IDebugEvent<EventArgs> m_programmingSucceededEvent = new DebugEvent<SuccessEventArgs>(ref m_eventSignal, programmingSuccessCode, this);
Candide
  • 30,469
  • 8
  • 53
  • 60
0

You need to create a covariant interface to allow this, by specifying the out keyword for the TArgs generic parameter:

public interface IDebugEvent<out TArgs> where TArgs : System.EventArgs
{ /* ... */ }

This is only supported on interfaces, so you need to make your DebugEvent class implement the covariant interface:

public class DebugEvent<TArgs> : IDebugEvent<TArgs> where TArgs:System.EventArgs 
{ /* ... */ }

This will allow you to assign an instance of a type which has its generic parameter type derived from the base System.EventArgs type.

IDebugEvent<EventArgs> evt = new DebugEvent<SuccessEventArgs>();
vgru
  • 49,838
  • 16
  • 120
  • 201
  • Well, actually only supported on interfaces *and delegates*, but the point is you cannot specify it for a class. – vgru May 30 '14 at 09:35
  • FYI: the statement `IDebugEvent evt = new DebugEvent();` needs to simply be `IDebugEvent evt = new DebugEvent();` – Candide May 30 '14 at 09:37