14

I have the following Classes / Interfaces:

// Model
public class A : IA { }
// ModelLogic
public class B : IB<A> { }

// Model Interface
public interface IA { }
// ModelLogic Interface
public interface IB<T> where T : IA { }

I try to create a new instance using the following code:

IB<IA> foo = new B();

I am getting the following error:

Cannot implicitly convert type 'B' to 'IB<IA>'. An explicit conversion exists (are you missing a cast?)

Can someone please explain why this is not possible?

3
  • What version of C# are you using? Commented Apr 27, 2012 at 14:34
  • 4
    Covariance and Contravariance FAQ and this blog series by Eric Lippert Commented Apr 27, 2012 at 14:35
  • 1
    B is an IB<A>, not an IB<IA>. Commented Apr 27, 2012 at 14:39

3 Answers 3

45

OK, let's replace A with Fish, IA with IAnimal, B with Aquarium, and IB<T> with IContainer<T>. And we'll add a member to IContainer<T>, and a second implementation of IAnimal:

// Model
public class Fish : IAnimal { }
public class Tiger : IAnimal { }
// ModelLogic
public class Aquarium : IContainer<Fish> 
{ 
    public Fish Contents { get; set; }
}

// Model Interface
public interface IAnimal { }
// ModelLogic Interface
public interface IContainer<T> where T : IAnimal 
{ 
    T Contents { get; set; }
}

IContainer<IAnimal> foo = new Aquarium(); // Why is this illegal?
foo.Contents = new Tiger(); // Because this is legal!

You can put a Tiger into foo -- foo is typed as a container that can contain any animal. But you can only put a Fish into an Aquarium. Since the operations you can legally perform on an Aquarium are different than the operations you can perform on an IContainer<IAnimal>, the types are not compatible.

The feature you want is called generic interface covariance and it is supported by C# 4, but you have to prove to the compiler that you will never put a tiger into your fish tank. What you want to do is:

// Model
public class A : IA { }
// ModelLogic
public class B : IB<A> { }

// Model Interface
public interface IA { }
// ModelLogic Interface
public interface IB<out T> where T : IA { }

Notice the covariance annotation on IB. This out means that T can only be used as an output, not as an input. If T is only an output then there is no way for someone to put a tiger into that fish tank because there is no "put into" property or method possible.

I wrote a number of blog articles while we were adding that feature to C#; if you are interested in the design considerations that went into the feature, see:

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/

Sign up to request clarification or add additional context in comments.

3 Comments

Invalid variance: The type parameter 'T' must be invariantly valid on 'xx.IContainer<T>.Contents'. 'T' is covariant. I am getting this error. I am new to covariant stuff. What does the error mean?
@Sandeep: You are somehow using T in an input position when you have said that you are only going to use it in an output position. Is Contents a property with a setter? If it is then clearly T is being used in an input position, and therefore the interface cannot be made covariant in T.
Of all the variance-related questions on SO, this is by far the best answer that makes the most sense. +1
1

To fix your code, just change

public interface IB<T> where T : IA { }

to

public interface IB<out T> where T : IA { }

Comments

0

It's not easy to see when you have empty interfaces. Consider you have one method M in interface IB:

public interface IB<T> where T : IA 
{ 
    void M(T t); 
}

And here is implementation of B:

public class B : IB<A>
{
    public void M(A t)
    {
        // only object of type A accepted 
    }
}

Then you have object C, which also implements IA:

public class C : IA { } 

So, if your code would be possible, then you could call:

IB<IA> foo = new B();
foo.M(new C());

Problem is that class B accepts only objects of type A. Error!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.