koryakinp
koryakinp

Reputation: 4125

C# Covariance and Contravariance

I have classes Message1D and Message2D both inherit from Message:

public abstract class Message {}
public class Message1D : Message {}
public class Message2D : Message {}

An interface IFoo and classes Bar and Baz, which implement it:

public interface IFoo<out G, in T> 
    where G : Message 
    where T : Message
{
    G PassMessage(T input);
}

public class Bar : IFoo<Message1D, Message1D>
{
    public Message1D PassMessage(Message1D input)
    {
        throw new NotImplementedException();
    }
}

public class Baz : IFoo<Message2D, Message2D>
{
    public Message2D PassMessage(Message2D input)
    {
        throw new NotImplementedException();
    }
}

And my problem is here. How can I add Foo and Bar instances to the list ?

public class Network
{
    private List<IFoo<Message, Message>> messages = new List<IFoo<Message, Message>>();

    public void AddBar()
    {
        messages.Add(new Bar());
    }

    public void AddBaz()
    {
        messages.Add(new Baz());
    }
}

I have an exception:

Can not convert Bar to IFoo<Message,Message>

and

Can not convert Baz to IFoo<Message,Message>

How can I add Bar and Baz instances to the List ?

Upvotes: 3

Views: 110

Answers (2)

Bagdan Imr
Bagdan Imr

Reputation: 1286

As @Servy mentioned, it is not possible in your current architecture and you should probably remake it.

But if somehow you can guarantee type safety, then you can try to explicitly implement IFoo<Message1D, Message> in addition to IFoo<Message1D, Message1D> as follows:

public class Bar : IFoo<Message1D, Message1D>, IFoo<Message1D, Message>
{
    public Message1D PassMessage(Message1D input)
    {
        // ...
    }

    Message1D IFoo<Message1D, Message>.PassMessage(Message input)
    {
        try
        {
            return PassMessage((Message1D) input);
        }
        catch (InvalidCastException)
        {
            // Message2D passed, handling exception
            // ...
        }
    }

Similarly implement IFoo<Message2D, Message> for Baz.

That will make your code work, but it is most likely not the best decision for you.

Upvotes: 0

Servy
Servy

Reputation: 203817

How can I add Bar and Baz instances to the List?

You can't, because you're trying to treat the input message as if it's covariant, but it's contravariant. You can treat an IFoo<Message1D, Message> as an IFoo<Message, Message>, but you can't treat an IFoo<Message, Message1D> as an IFoo<Message, Message>. If that were valid, then someone would be allowed to pass in 2D messages into your object that can only handle 1D messages.

Upvotes: 4

Related Questions