4

可能重复:
C# 是否支持返回类型协方差?
为什么我不能以这种方式实现接口?

考虑以下:

public interface IAnimal {
}

public class Animal : IAnimal {
}

public interface ICage {
     IAnimal SomeAnimal {get;}
}

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

我已经阅读了很多关于 IEnumerable 的协变和逆变的内容,但我不确定如何让上述代码工作。我收到错误“Cage 未实现接口成员 IAnimal”。由于它定义了比 IAnimal 更定义的 Animal,因此协方差似乎应该照顾我。

我错过了什么?提前致谢。

4

4 回答 4

6

这在 C# 中目前是不可能的。

理论上,语言设计者可以添加它,只是他们还没有[还]。他们可能会也可能不会决定将它添加到 C# 的潜在未来版本中。

最好的解决方法可能是:

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

    IAnimal ICage.SomeAnimal
    {
        get { return SomeAnimal }
    }
}
于 2012-12-27T15:55:08.570 回答
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.

于 2012-12-27T15:55:11.710 回答
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.

于 2012-12-27T16:00:21.797 回答
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.

于 2012-12-27T16:08:35.663 回答