关于c#:如何在新实现的接口或基类之间做出决定?

How to decide between an Interface or Base Class for an new implementation?

在实现方面,我应该如何决定使用基类型或接口?我试着做了几个例子,但我不完全理解:(

关于如何以及为什么会受到高度赞赏的例子。


基类abstract或not可以包含实现的成员。接口不能。如果您的所有实现都将执行类似的操作,那么一个基类可能是解决问题的方法,因为您的所有子类都可以共享基类上成员的相同实现。如果他们不打算共享实现,那么接口可能就是解决问题的方法。

例子:

1
2
3
4
5
6
7
8
9
class Person
{
    string Name { get; set; }
}

class Employee : Person
{
    string Company { get; set; }
}

员工从人员继承是有意义的,因为员工类不必定义Name属性,因为它共享实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
interface IPolygon
{
    double CalculateArea()
}

class Rectangle : IPolygon
{
    double Width { get; set; }
    double Height { get; set; }

    double CalculateArea()
    {
        return this.Width * this.Height;
    }
}

class Triangle : IPolygon
{
    double Base { get; set; }
    double Height { get; set; }

    double CalculateArea()
    {
        return 0.5 * this.Base * this.Height;
    }
}

由于RectangleTriangle的实现与CalculateArea的实现是如此不同,所以它们从基类继承是没有意义的。

如果您创建了一个基类,并且发现在中只包含抽象成员,那么您也可以使用一个接口。

而且,正如J_uu m所说,您不能从多个基类继承,但可以实现多个接口。

我通常首先定义接口,如果我发现自己在实现中复制了代码,我会创建一个实现接口的基类,并使我的实现从中继承。


为了决定是使用抽象类还是接口,我发现本文非常有用的来源:

A good way to distinguish between a case for the one or the other for me has always been the following:

  • Are there many classes that can be"grouped together" and described by one noun? If so, have an abstract class by the name of this noun, and inherit the classes from it. (A key decider is that these classes share functionality, and you would never instantiate just an Animal... you would always instantiate a certain kind of Animal: an implementation of your Animal base class)
    Example: Cat and Dog can both inherit from abstract class Animal, and this abstract base class will implement a method void Breathe() which all animals will thus do in exactly the same fashion. (I might make this method virtual so that I can override it for certain animals, like Fish, which does not breath the same as most animals).

  • What kinds of verbs can be applied to my class, that might in general also be applied to others? Create an interface for each of these verbs.
    Example: All animals can be fed, so I will create an interface called IFeedable and have Animal implement that. Only Dog and Horse are nice enough though to implement ILikeable - I will not implement this on the base class, since this does not apply to Cat.

  • 请看一下这个接口和基类的问题。


    使用抽象类的原因之一是我们必须强制一些初始化(比如通过构造函数的状态)。

    接口不允许您定义构造函数的协定。

    在下面的示例中,每个动物对象都应该有一个名称。这无法通过接口强制执行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    public abstract class Animal
    {
        public Animal(string name)
        {
            this.Name = name;
        }

        public string Name
        {
            get;
            private set;
        }
    }

    public class Cat : Animal
    {
        public Cat(string name)
            : base(name)
        {

        }

        string NoOfLegs { get; set; }
    }



    class Program
    {
        static void Main(string[] args)
        {
            Animal aCat = new Cat("a");
        }
    }


    实际上,它们并不一定是相互排斥的。根据代码实现的发展情况,可以同时使用这两种方法。
    BR/>接口通常是合同的表述。它定义了一个预期的行为,实现者应该尊重它。通常认为,根据接口编写公共API代码是一种好的实践。这样可以减少与实现细节的耦合,并允许更容易地重构和维护代码。现在有资格成为公共API的是软件组件,这些组件体现了在设计中定义的高级交互,并且打算在同一个项目中或在几个独立范围的项目中由您自己和其他人重用。
    BR/>基类已经是实现的一部分。是否实现接口。它将其潜在的层次结构与特定的实现细节联系起来:对象状态、可重写或不可重写的方法等。


    接口是一种更灵活的结构。您只能有一个基类,但可以实现多个接口。如果您需要一个对象来支持多个行为,但是其中多个行为需要一个特定的基类,那么您将无法这样做。

    正如Dan所指出的,在许多语言中,基类有一个便利的优势,因为您可以提供一个基本实现。要使用接口实现这一点,需要创建一个提供基本实现的类,然后手动将每个接口方法的实现委托给该类——这几乎不太方便。


    我发现一个类比在这里有帮助:

    抽象基类:汽车制造商可以开发一种汽油发动机,它有一些变型(1.6,2L等)。可以将引擎块强制转换视为抽象基类,即它定义了引擎的基本形状和特征。2L版本可能有更大的气缸盖等。

    接口:发动机还可以使用各种现成的部件,例如交流发电机、散热器、起动马达等,因此必须实现这些部件定义的接口。这些部件通常是在不了解可能使用它们的发动机的情况下设计的。