接口指定的属性的多态性

时间:2010-09-08 16:53:33

标签: c# c#-4.0 polymorphism

为什么这不起作用?

 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...
}

[编译器消息告诉我它为什么会中断,但我想知道为什么编译器不允许这样做的原因 - 即使有任何解决方法? - 谢谢]

4 个答案:

答案 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界面的成员。