abstract class A<T> {
List<T> Items { get; set; }
}
class B {}
class C : A<B> {}
class D : B {}
class E : A<D> {}
static class X {
public A<B> GetThing(bool f) {
return f ? new E() : new C();
}
}
无法确定条件表达式的类型,因为“ConsoleApplication4.E”和“ConsoleApplication4.C”之间没有隐式转换
现在我得到了“为什么”(我认为),但我看不出如何编译。我想我必须创建一个定义某种方差的接口并从中继承,但我不确定。但无论如何,E()
应该继承自C()
。
任何参赛者?
TIA
答案 0 :(得分:15)
要在C#中使用通用差异,您必须满足所有以下条件:
您的计划符合条件1和条件5,但不符合条件2,3或4。
无法获得您想要的差异,因为您想要的东西会违反条件4 。观察当我们满足除条件4之外的所有条件时会发生什么:
// Suppose this declaration were legal:
interface IMyCollection<out T>
{
List<T> Items { get; set; }
}
class Animal {}
class AnimalCollection : IMyCollection<Animal>
{
public List<Animal> { get; set; }
}
class Giraffe : Animal {}
class GiraffeCollection : IMyCollection<Giraffe>
{
public List<Giraffe> { get; set; }
}
static class X
{
public IMyCollection<Animal> GetThing()
{
// IMyCollection is covariant in T, so this is legal.
return new GiraffeCollection();
}
}
class Tiger : Animal {}
...
IMyCollection<Animal> animals = X.GetThing();
// GetThing() actually returns a GiraffeCollection
animals.Items = new List<Animal>() { new Tiger(); }
长颈鹿系列现在包含一个包含老虎的动物名单。
如果要使用方差,则必须是类型安全的。因为编译器无法将方差注释确定为类型安全,所以它拒绝了IMyCollection的声明。
让我们看看我们如何做到这一点并保持类型安全。 问题是项目可以通过界面写入。
interface IMyCollection<out T>
{
IEnumerable<T> Items { get; }
}
class Animal {}
class AnimalCollection : IMyCollection<Animal>
{
public IEnumerable<Animal> { get { yield return new Tiger(); } }
}
class Giraffe : Animal {}
class GiraffeCollection : IMyCollection<Giraffe>
{
public IEnumerable<Giraffe> { get { yield return new Giraffe(); } }
}
static class X
{
public IMyCollection<Animal> GetThing()
{
return new GiraffeCollection();
}
}
class Tiger : Animal {}
...
MyCollection<Animal> animals = X.GetThing();
// GetThing() actually returns a GiraffeCollection
foreach(Animal animal in animals.Items) { ... }
// Items yields a giraffe, which is an animal
完全类型安全。这将是一个合法的C#4计划。
如果C#4中协方差和逆变的设计细节让您感兴趣,您可以考虑阅读关于该主题的十几篇文章。你可以在这里找到它们:
http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/
请注意,这些是按照从最近到最近的顺序列出的;从底部开始。
如果您特别感兴趣的是确定界面注释何时有效的规则,请参阅本文(我刚刚发现并修正了一些错误,所以我很高兴我们进行了这次对话。)
http://blogs.msdn.com/b/ericlippert/archive/2009/12/03/exact-rules-for-variance-validity.aspx
答案 1 :(得分:3)
E
和C
之间没有关系。因此,您的返回类型必须为A<B>
,这可能被视为这些类型的公共父级。此外,A
必须是一个接口,其中泛型参数定义为out
,以便工作:
interface A<out T> { }
class B { }
class C : A<B> { }
class D : B { }
class E : A<D> { }
static class X
{
public static A<B> GetThing(bool f)
{
if (f)
{
return new E();
}
return new C();
}
}
无法使其与ternary operator (?:
)一起使用。编译器根本不喜欢它。
更新:
在@Eric Lippert在评论部分中的优秀评论之后,您可以使用三元运算符转换为A<B>
:
public static A<B> GetThing(bool f)
{
return f ? (A<B>)new E() : new C();
}
答案 2 :(得分:0)
这不起作用,因为C
和E
没有共同的父母。对于这样的事情,你需要创建一个共同的父类型,无论是类还是接口。