我可以用派生类型覆盖吗?

时间:2008-10-01 11:03:24

标签: c# .net inheritance covariance

据我所知,无法在C#2.0中执行以下操作

public class Father
{
    public virtual Father SomePropertyName
    {
        get
        {
            return this;
        }
    }
}

public class Child : Father
{
    public override Child SomePropertyName
    {
        get
        {
            return this;
        }
    }
}

我通过在派生类中将属性创建为“new”来解决问题,但当然这不是多态的。

public new Child SomePropertyName

2.0中有什么解决方案吗? 3.5中解决此问题的任何功能如何?

9 个答案:

答案 0 :(得分:41)

您可以重新声明(新),但不能同时重新声明和覆盖(使用相同的名称)。 一种选择是使用受保护的方法来隐藏细节 - 这允许同时进行多态和隐藏:

public class Father
{
    public Father SomePropertyName
    {
        get {
            return SomePropertyImpl();
        }
    }
    protected virtual Father SomePropertyImpl()
    {
        // base-class version
    }
}

public class Child : Father
{
    public new Child SomePropertyName
    {
        get
        { // since we know our local SomePropertyImpl actually returns a Child
            return (Child)SomePropertyImpl();
        }
    }
    protected override Father SomePropertyImpl()
    {
        // do something different, might return a Child
        // but typed as Father for the return
    }
}

答案 1 :(得分:25)

由于类型安全问题,这在任何.NET语言中都是不可能的。在类型安全的语言中,您必须为返回值和参数的逆变提供协方差。拿这个代码:

class B {
    S Get();
    Set(S);
}
class D : B {
    T Get();
    Set(T);
}

对于Get方法,协方差意味着T必须是S或从S派生的类型。否则,如果您对类型为D的对象的引用存储在变量类型B中,则当您调用B.Get()时,您将无法将对象表示为S反击 - 打破类型系统。

对于Set方法,逆变意味着T必须是SS派生的类型。否则,如果您引用了存储在变量类型D中的B类型的对象,则当您调用B.Set(X)时,其中X的类型为S但不是类型TD::Set(T)会得到一个它没有预料到的类型的对象。

在C#中,有意识地决定在重载属性时禁止更改类型,即使它们只有一个getter / setter对,因为否则会有非常不一致的行为(“你的意思是,我可以使用getter更改一个类型,但不能同时使用getter和setter更改类型?为什么不呢?!?“ - Anonymous Alternate Universe Newbie)。

答案 2 :(得分:11)

不,但您可以在2及以上版本中使用泛型:

public class MyClass<T> where T: Person
{
    public virtual T SomePropertyName
    {
        get
        {
            return  ...;
        }
    }
}

然后父亲和孩子是同一类的通用版本

答案 3 :(得分:7)

来自Wikipedia

  

在C#编程语言中,支持return-type   协方差和参数   增加了代表们的逆转   在该语言的2.0版本中。   既不是协方差也不是逆变   支持方法覆盖。

虽然没有明确说明属性的协方差。

答案 4 :(得分:2)

您可以为父子创建一个公共接口,并返回该接口的类型。

答案 5 :(得分:1)

没有。 C#不支持这个想法(它被称为“返回类型协方差”)。 但是你可以这样做:

public class FatherProp
{
}

public class ChildProp: FatherProp
{
}


public class Father
{
    public virtual FatherProp SomePropertyName
    {
        get
        {
            return new FatherProp();
        }
    }
}


public class Child : Father
{
    public override FatherProp SomePropertyName
    {
        get
        {
            // override to return a derived type instead
            return new ChildProp();
        }
    }
}

即。使用基类定义的契约,但返回派生类型。我已经做了一个更详细的例子来使这一点更清楚 - 再次返回“this”不会改变任何东西。

对于它的实际类型(即“if someObject is ChildProp”)测试返回的对象是可能的(但是很麻烦),但最好在它上面调用一个对其类型做正确事情的虚拟方法。 / p>

基类虚方法(在本例中为虚拟属性)不仅具有实现,还定义了一个契约:如果子类符合此契约,则子类可以提供SomePropertyName的不同实现(即SomePropertyName返回一个对象)类型“FatherProp”)。返回源自“FatherProp”的“ChildProp”类型的对象符合此合同。但是你不能改变“孩子”中的合同 - 这个合同适用于所有来自“父亲”的类。

如果您退后一步并查看更广泛的设计,C#工具箱中还有其他语言结构,您可能还需要考虑这些结构 - 泛型或接口。

答案 6 :(得分:1)

  

没有。 C#不支持这个想法   (它被称为“返回类型   协方差“)。

     

来自维基百科:

     

在C#编程语言中,   支持返回类型   协方差和参数   增加了代表们的逆转   在该语言的2.0版本中。   既不是协方差也不是逆变   支持方法覆盖。

     

你可以重新声明(新),但是你   无法重新声明和覆盖   同一时间(同名)。一   选项是使用受保护的方法   隐藏细节 - 这允许两者   多态性和隐藏在同一个   时间:

最好的解决方案是使用泛型:

public class MyClass<T> where T: Person
{
   public virtual T SomePropertyNameA
   {        
      get { return  ...; }    
   }
}//Then the Father and Child are generic versions of the same class

答案 7 :(得分:1)

这是我能来的最近的(到目前为止):

    public sealed class JustFather : Father<JustFather> {}

    public class Father<T> where T : Father<T>
    { public virtual T SomePropertyName
        { get { return (T) this; }
        }
    }

    public class Child : Father<Child>
    { public override Child SomePropertyName
        { get { return  this; }
        }
    }

如果没有JustFather类,除非是其他派生类型,否则无法实例化Father<T>

答案 8 :(得分:0)

现代答案

C# 9 开始,return type covariance is supported。这是从该链接复制的基本示例:

class Compilation ...
{
    public virtual Compilation WithOptions(Options options)...
}

class CSharpCompilation : Compilation
{
    public override CSharpCompilation WithOptions(Options options)...
}