使用更多派生类型重写子类继承的属性

本文关键字:子类 继承 属性 重写 类型 派生 | 更新日期: 2023-09-27 18:35:43

我试图实现的目标的简化示例如下所示:

public class Animal
{
    public virtual Teeth teeth {get;set;}
}
public class Mouse : Animal
{
    public override SmallTeeth teeth {get; set;} // SmallTeeth Inherits from Teeth
}

这显然不起作用,因为牙齿必须与动物类中的类型相同才能在鼠标类中被覆盖。但是,是否可以实现这样的事情,允许我在从 Animal 继承的任何函数中使用更派生的类型?例如,如果 Animal 类包含一个咬人的函数:

public void Bite()
{
    teeth.bite()
    Console.WriteLine("Ouch")
} 

我可以调用从 Animal 继承的 Bite() 函数,它将使用 Mouse 类的类型为 SmallTeeth 的字段。这可能吗?这是做我想做的事情的最佳方式吗?如果不是,解决这个问题的正确方法是什么?

使用更多派生类型重写子类继承的属性

注意:这个答案写于 2016 年。从那以后的几年里,返回类型协方差(令人惊讶!)被添加到 C# 中。阅读此答案时请记住这一点。

<小时 />

所需的功能称为返回类型协方差,C# 不支持它。 (顺便说一下,C++确实如此。

对于协变返回类型,通常的情况是:

abstract class Animal
{
    public abstract Cage GetCage();
}
public class Fish : Animal
{
    public override Aquarium GetCage() { ... }
}

这是不合法的,但如果它是合法的,那将是安全的。 也就是说,如果你手里有一只动物,你要求一个笼子,你会得到一个,即使它是一条鱼。为什么?因为水族馆是一种笼子。

你的建议不仅是非法的,而且不安全:

Animal animal = new Mouse();
animal.Teeth = new TRexTeeth();

合同是,可以用任何类型的牙齿调用二传手。通过使派生类在可以接受的内容上更具限制性,您将违反基类的协定。

所以不要那样做。

不过,有很多方法可以在 C# 中实现您想要的东西。

这里只是其中之一:

interface IAnimal
{
    Teeth Teeth { get; } // READ ONLY
}
class Mouse : IAnimal
{
    private SmallTeeth smallTeeth;
    public SmallTeeth Teeth 
    {
        get { return smallTeeth; }
    }
    Teeth IAnimal.Teeth { get { return this.Teeth; } }
}

现在,如果您将鼠标投射到 IAnimal,您将获得返回 Tooth 的属性,如果您正常使用鼠标,您将获得返回 SmallTooth 的属性。

我在这里描述了解决此问题的另一种方法:

C# 是否支持返回类型协方差?

另一个答案中给出的通用解决方案也有效,尽管我个人更愿意将泛型排除在外,除非必要。

搜索"C# 中的返回类型协方差"以获取有关此模式的详细信息。

其实是的。您可以不应该为此目的使用generics,包括类型约束(请参阅下面的评论Eric Lippert 的帖子,他详细解释了您在您的情况下想要或试图实现的目标):

public class Animal<T>  where T : Teeth
{
    public virtual T teeth {get;set;}
}
public class Mouse : Animal<SmallTeeth>
{
    public override SmallTeeth teeth {get; set;} // SmallTeeth Inherits from Teeth
}