0

Why doesn't this work and how do I make it work? In this case M directly implements IMessage and I get a InvalidCastException.

public void Subscribe<M>(IMessageListener<M> listener) where M : IMessage
{
    IMessageListener<IMessage> l = (IMessageListener<IMessage>)listener;
}

Shouldn't casting IMessageListener<M> to IMessageListener<IMessage> work when M implements IMessage?

Sorry for the bad title, didn't know how to describe it.

//EDIT The IMessageListener class looks like this:

public interface IMessageListener<M> where M : IMessage
{
    void ProcessMessage(M message);
}
Zoidy
  • 3
  • 3
  • Oops! Sorry about that misleading answer - I just posted it based on something I read up sometime back. Hope this answer helps you - http://stackoverflow.com/questions/19166133 – potatopeelings Jul 11 '15 at 13:42
  • Why do you need to cast it? Just use it as an `IMessageListener` – call `listener.ProcessMessage(message)` later. – Vlad DX Jul 11 '15 at 13:56

1 Answers1

0

Short answer: no, it shouldn't work.

public class MessageA : IMessage
{
    public int SpecificAField;
}

public class MessageB : IMessage {}

public class MessageAListener : IMessageListener<MessageA>
{
    public void ProcessMessage(MessageA message)
    {
        messageA.SpecificAField = 3;
    }
}

If the compiler were to let Subscribe<MessageA>? cast the MessageAListener to IMessageListener<IMessage>, then you could pass it a MessageB.

Then the MessageAListener would try to set SpecificAField on the MessageB, and it doesn't have one.


This doesn't work because IMessageListener<T> is contravariant on T - it references T solely as an input. Thus you can cast an IMessageListener<IMessage> to an IMessageListener<MessageA> because it will happily accept a MessageA as an input in place of an IMessage.

The cast you want to perform would require IMessageListener<T> to be covariant on T - it would have to reference T solely as an output. You could then cast an IMessageListener<MessageA> to an IMessageListener<IMessage> as the true return type, MessageA, can be converted to the expected return type, IMessage.

Rawling
  • 49,248
  • 7
  • 89
  • 127
  • Ok, I get that. My problem is, that I have a Dictionary with a Type as key and a list of instances of the type as the value, that implements IMessage. How would I implement that then? – Zoidy Jul 11 '15 at 13:54
  • Do you know where I could read more about covariance? Its totally new to me – Zoidy Jul 11 '15 at 13:58
  • @Zoidy Mixing generics and type objects is usually a mistake. Generics handle different types at compile time; type objects handle different types at run time. Eric Lippert has a good series on covariance starting [here](http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-contravariance-in-c-part-one.aspx). – Rawling Jul 11 '15 at 13:59