4

Possible Duplicate:
Does C# support return type covariance?
Why can’t I implement an Interface this way?

Consider the following:

public interface IAnimal {
}

public class Animal : IAnimal {
}

public interface ICage {
     IAnimal SomeAnimal {get;}
}

public class Cage : ICage{
     public Animal SomeAnimal { get; set; }
}

I've read a lot of stuff on covariance and contravariance for IEnumerable, but I'm not sure how to get the above code to work. I get the error "Cage does not implement interface member IAnimal". Since it defined Animal, which is more defined than IAnimal, it seems like covariance should take care of me.

What am I missing? Thanks in advance.

Community
  • 1
  • 1
reustmd
  • 3,513
  • 5
  • 30
  • 41

4 Answers4

6

That's not currently possible in C#.

It theoretically possible for the language designers to add it, they just haven't [yet]. They may or may not decide to add it to a potential future version of C#.

The best workaround would probably be:

public class Cage : ICage
{
    public Animal SomeAnimal { get; set; }

    IAnimal ICage.SomeAnimal
    {
        get { return SomeAnimal }
    }
}
Servy
  • 202,030
  • 26
  • 332
  • 449
  • Good answer. I selected Dave's though because he links directly to EricL giving more background. – reustmd Dec 27 '12 at 16:09
2

Quoted from Eric Lippert, in response to me asking the same question here: Why can't I implement an Interface this way?

C# does not support return type covariance for the purposes of interface implementation or virtual method overrding. See this question for details:

Does C# support return type covariance?

C# does support generic covariance and contravariance of interfaces and delegate types that are constructed wtih reference types for the type arguments as of C# 4.

And C# does support return type covariance when converting a method that returns a reference type to a delegate type whose return type is a compatible reference type. (And similarly it supports parameter type contravariance.)

If this subject interests you, I have written a great many articles discussing various versions of variance that C# does and does not support. See

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

for details.

Community
  • 1
  • 1
Dave Bish
  • 19,263
  • 7
  • 46
  • 63
2
public interface IAnimal {
}

public class Animal : IAnimal {
}

public interface ICage {
     IAnimal SomeAnimal {get;}
}

public class Cage : ICage{
     public Animal SomeAnimal { get; set; }
}

public class AnotherAnimal : IAnimal {
}

Cage c = new Cage();
ICage ic = (ICage)c;
ic.Animal = new AnotherAnimal();

this would be invalid because AnotherAnimal implements IAnimal, but it is not an Animal.

EDIT

the above would only be relevant if there was a setter defined in the interface. Because there is not, the proper answer here is that the desired functionality cannot be achieved in C#; it is a feature not included in the language.

samandmoore
  • 1,221
  • 2
  • 15
  • 23
  • `ICage`'s property doesn't have a setter in the OP. – Servy Dec 27 '12 at 16:04
  • true, without a setter this would not be a problem and should be allowed, but Eric Lippert's post says that it is just not supported in C#. – samandmoore Dec 27 '12 at 16:06
  • Yes, I'm aware of that, but none of that is in your answer. The OP specifically ensured that `ICage` *doesn't* have a getter, which logically would make using covariance possible, it's simply a feature not included in the language. Thus, the answer is, "it's a feature that hasn't been included in the language" and not "it's not possible to implement because of [...]." – Servy Dec 27 '12 at 16:13
2

To get covariance you should have something like this:

public interface IAnimal {
}

public class Lion : IAnimal {}
public class Sheep : IAnimal {}

// note the "out" on the T type parameter
public interface ICage<out T> where T:IAnimal {
     T SomeAnimal {get;}
}

public class Cage<T> : ICage<T> where T:IAnimal {
     public T SomeAnimal { get; set; }
}

you can now do this:

// without covariance on ICage you can't assign a 'Cage<Sheep>' to 'ICage<IAnimal>'
ICage<IAnimal> sheeps = new Cage<Sheep>() {SomeAnimal=new Sheep()};
ICage<IAnimal> lions = new Cage<Lion>() {SomeAnimal=new Lion()};

or this (creating a heterogeneous list of Cage<IAnimals> with both Cages of Sheeps and Cages of Lions), which is an equivalent but probably more useful example:

// without covariance on ICage it errors: cannot convert from 'Cage<Sheep>' to 'ICage<IAnimal>'
var zoo = new List<ICage<IAnimal>>{
     new Cage<Sheep> {SomeAnimal=new Sheep()},
     new Cage<Lion> {SomeAnimal=new Lion()},
};

if you try removing the out from the declaration of ICage you will see the difference.

Paolo Falabella
  • 24,914
  • 3
  • 72
  • 86