不确定何时使用抽象属性及何时不用

63

我不太确定在抽象类和属性中哪个更好看,或者在什么情况下应该使用抽象类和属性,何时应该使用非抽象属性。我来尝试举一个简单的例子。假设我有以下代码:

abstract class Human
{
  public GenderType Gender { get; set; }
  public string Name { get; set; }
  public Date Born { get; set; }
  public bool IsNerd { get; set; }

  abstract public void Speak();
  abstract public void Sleep();
  abstract public void AnoyingPeopleOnStackOverflow();
  //... so on
}

class Peter : Human
{
  //Peter is special, he got a second name
  //But thats all, everything else is the same as like on other humans
  public string SecondName { get; set; }

  //...override abstract stuff
}

这样做可以吗?据我所理解,如果不想覆盖属性,则不必使用抽象属性。在这种情况下,只需像 SpeakSleep 这样的方法应该是抽象的即可。

那么,如果这样可以,什么情况下应该使用抽象属性呢?


1
何时应该使用抽象属性?当您想要断言子类必须提供特定方法实现时。 - zerkms
1
  1. 如果这是 Java 的话,可能就不会有任何问题了。你可能只需要使用一个“接口”即可。这在 C# 中本质上就是你正在做的事情,对吗?
  2. 我个人认为,如果它需要成为“协议”的一部分,那么在抽象类中声明它是合适的。换句话说,我觉得你所做的是完全可以的。在我看来...
- paulsm4
2
@paulsm4,你也可以用C#来做到这一点。 - Jon Hanna
5个回答

101

当您没有默认实现且派生类必须实现时,请使用抽象属性。

当您在基类中有一个实现但想允许重写时,请使用虚拟属性。

使用 override 关键字来覆盖成员,如果不希望再次被覆盖,请将成员标记为 sealed override

如果不希望属性被重写,请不要将其标记为 abstractvirtual

使用 new 关键字隐藏非抽象、非虚拟的成员(这很少是一个好主意)。

如何:定义抽象属性

我发现在设计中经常会出现需要类型特定逻辑和/或副作用的抽象属性。基本上你在说,“这里是所有子类必须具有的数据点,但我不知道如何实现它”。然而,包含大量逻辑和/或引起副作用的属性可能不理想。这是一个重要的考虑因素,尽管没有固定的正确/错误方法。

参见:

就我个人而言,我经常使用抽象方法,但很少使用抽象属性。


47

我知道我想让他们做什么,但我不关心他们如何实现:接口。

我知道我想让他们做什么,但我不关心他们如何完成其中的一些部分,但我对他们将如何完成其他部分有明确的想法:抽象类。

我知道我想让他们做什么,也知道大多数情况下他们会怎样做:具有虚成员的具体类。

还可以有其他情况,例如没有抽象成员的抽象类(无法创建实例,但是它提供的功能是完整的),但这种情况比较罕见,通常是因为特定的层次结构非常适合解决给定的问题。

(顺便说一句,我不认为 Peter 是人类的一种类型,而是每个名为 Peter 的实例都恰好是一个人类。以这种方式挑选示例代码并不公平,但当你思考这类问题时,这比平常更相关)。


3
+1 - 同意,Peter 应该是 Human 的一个子类的实例,例如 HumansWithTwoNames - Tim M.
2
@TimMedora 是的,我们在编写示例代码时都会想到一些糟糕的名称,通常这并不重要,但这是一个比大多数问题更为重要的问题。 - Jon Hanna

20

抽象成员就是必须要被覆盖的虚拟成员。你可以使用它来实现某些必须在基类中实现,但无法在基类中实现的内容。

如果您想创建一个虚拟属性,并希望它必须在继承您的类的类中被覆盖,那么您可以将其设置为抽象属性。

例如,如果您有一个动物类,它的呼吸能力无法仅从它是动物这个信息中确定,但这是非常关键的内容:

public abstract class Animal {

  public abstract bool CanBreathe { get; }

}

对于一条鱼和一只狗,实现方式将不同:

public class Dog : Animal {

   public override bool CanBreathe { get { return !IsUnderWater; } }

}

public class Fish : Animal {

   public override bool CanBreathe { get { return IsUnderWater; } }

}

8

当所有子类都必须实现方法/属性时,请使用抽象类。如果并非每个子类都需要实现它,则不要使用它。

至于您的示例,如果不是每个人都需要SecondName,则没有必要在基类中创建抽象属性。另一方面,如果每个人都需要第二个名字,则将其作为抽象属性。

抽象属性的正确用法示例:

public class Car
{
    public abstract string Manufacturer { get; }
}

public class Odyssey : Car
{
    public override string Manufacturer
    {
         get 
         {
             return "Honda";
         }
    }
}

public class Camry : Car
{
    public override string Manufacturer
    {
         get 
         {
             return "Toyota";
         }
    }
}

使“Maker”成为抽象的是正确的,因为每辆车都有制造商,并且需要告诉用户该制造商是谁。

2
抽象属性将用于您希望类始终公开该属性,但无法确定该属性的实现方式的情况 - 将其留给/强制继承类来执行。
这里有一个例子here,其中抽象类名为Shape,并公开了一个抽象的Area属性。您无法在基类中实现Area属性,因为每种类型的形状的面积公式都会改变。所有形状都有一个面积(某种程度上),因此所有形状都应公开该属性。
您的实现本身看起来很好。试图想出一个合理的Human抽象属性示例,但无法想到任何合理的东西。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接