为什么这不起作用?
public class ClassOptions {}
public interface Inode {
ClassOptions Options {get;}
}
public class MyClass : Inode {
public ClassOptions Options { get; set; }
}
public class ClassDerivedOptions : ClassOptions {
}
public class MyDerivedClass : Inode {
public ClassDerivedOptions Options { get; set; } << does not implement INode...
}
[编译器消息告诉我它为什么会中断,但我想知道为什么编译器不允许这样做的原因 - 即使有任何解决方法? - 谢谢]
答案 0 :(得分:8)
它不起作用,因为INode接口显式调用ClassOptions类型的Options属性。 C#不支持返回类型协方差(这是你在这种情况下要求的)。
对于它的价值,Microsoft Connect上还有一个特定于返回类型协方差的语言功能请求:
Need covariant return types in C# / all .NET languages
如果您查看该页面,他们还会提到常见的解决方法是使用显式接口实现:
public class MyDerivedClass : INode
{
public ClassDerivedOptions Options { get; set; }
public ClassOptions INode.Options { get { return Options; } }
}
答案 1 :(得分:4)
正如Justin所说,你想要的功能被称为“返回类型协方差”,它在C#中不受支持,或者就此而言,在CLR类型系统中不受支持。
虽然经常被要求,但是这个功能很快就会实现(*)。由于CLR不支持它,因此为了实现它,我们只需生成所有为您进行呼叫转发的帮助方法。由于您已经可以使用少量代码“手动”执行此操作,因此编译器为您执行此操作几乎没有增加任何附加值。 (和as another question today notes, people sometimes get confused or irritated when the compiler generates a method to do interface forwarding on your behalf。)
别误会我的意思;我可以看到它是如何派上用场的,我在C ++中使用了这个功能。但是每当它出现在C#程序中时,我发现我可以很容易地解决它的缺失问题。
(*)当然五年前我会对命名参数和可选参数说完全一样的东西,现在它们在C#4中。 可能是一个不太可能的特性实施,但需求必须相当高。
答案 2 :(得分:3)
它不起作用,因为接口定义了一个契约,当您实现此契约方法时,签名必须完全匹配。可能的解决方法是使用通用接口:
public class ClassOptions
{ }
public class ClassDerivedOptions : ClassOptions
{ }
public interface INode<T> where T : ClassOptions
{
T Options { get; }
}
public class MyClass : INode<ClassOptions>
{
public ClassOptions Options { get; set; }
}
public class MyDerivedClass : INode<ClassDerivedOptions>
{
public ClassDerivedOptions Options { get; set; }
}
答案 3 :(得分:1)
处理这种情况的标准方法是明确地实现接口:
public class MyDerivedClass : Inode {
// New, more specific version:
public ClassDerivedOptions Options { get; set; }
// Explicit implementation of old, less specific version:
ClassOptions Inode.Options
{
get { return Options; }
}
}
这是大多数旧IList
实现在泛型之前的工作方式,例如:指定更具体的T this[int index]
属性,然后显式实现object IList.this[int index]
,在{{1}时抛出异常用一个错误类型的对象调用。
在您发布的示例代码中,您甚至不需要显式set
,因为它不是您的set
界面的成员。