为什么抽象类和接口都存在于C#中?

时间:2009-06-22 16:48:01

标签: c# .net oop

如果我们可以通过使类中的所有成员都是抽象的来实现接口功能,那么为什么抽象类和接口都存在于C#中。

是因为:

  1. 存在具有多重继承的接口
  2. 有接口是有道理的,因为对象的CAN-DO功能应放在接口而不是基本抽象类中。

请澄清

11 个答案:

答案 0 :(得分:27)

好吧,抽象类可以指定一些实现,但通常不是全部。 (已经说过,完全有可能提供一个没有抽象成员的抽象类,但是很多虚拟的抽象类都带有“no-op”实现)。接口提供 no 实现,仅提供合同。

你当然可以争辩说,如果允许类的多重继承,接口将毫无意义。

就个人而言,我并没有因为继承而在整个“是 - ”与“可以做”的区别。它从来没有给我一个关于做什么的直觉,因为只是玩弄不同的想法,看看哪些人觉得最灵活。 (再说一遍,我非常喜欢“继承遗产”的人......)

编辑:正如在评论中反驳lbushkin第三点的最方便的方法......你可以通过密封它来覆盖一个非虚拟的抽象方法(就无法进一步覆盖它而言):

public abstract class AbstractBase
{
    public abstract void Foo();
}

public class Derived : AbstractBase
{
    public sealed override void Foo() {}
}

Derived派生的类不能再覆盖Foo

我并不以任何方式暗示我想要实现多重继承 - 但是如果我们 拥有它(以及它的复杂性)那么只是的抽象类包含抽象方法将完成几乎接口所做的一切。 (这是显式接口实现的问题,但这是我现在所能想到的。)

答案 1 :(得分:15)

这不是一个微不足道的问题,这是一个非常好的问题,我总是问我采访的任何候选人。
简而言之 - 抽象基类定义类型层次结构,而接口定义合同。

您可以看到 vs 实现Account可以是抽象基本帐户,因为您可以拥有CheckingAccountSavingsAccount等所有来自抽象基类Account的帐户。抽象基类也可以包含非抽象方法,属性和字段,就像任何普通类一样。但是,接口包含必须实现的抽象方法和属性。

c#let你只从一个基类派生 - 单一继承就像java一样。但是,您可以根据需要实现任意数量的接口 - 这是因为接口只是您的类承诺实现的合同。

因此,如果我有一个课程SourceFile,那么我的班级可以选择实施ISourceControl,其中写着“我忠实地承诺实施ISourceControl要求的方法和属性”

这是一个很大的领域,可能比我给出的更好,但我的时间很短但我希望有所帮助!

答案 2 :(得分:8)

它们都存在,因为它们都是非常不同的东西。抽象类允许实现,而接口则不允许。界面非常方便,因为它允许我说出我正在构建的类型(它是可序列化的,可食用的等),但它不允许我为其定义任何实现我定义的成员。

抽象类在某种意义上更强大,它允许我通过抽象和虚拟成员创建继承接口,但如果我选择的话,还提供某种默认或基本实现。然而,正如蜘蛛侠知道的那样,由于抽象类在架构上更加脆弱,因此强大的力量带来了巨大的责任。

旁注: 值得注意的是,Vance Morrrison(CLR团队)推测在未来版本的CLR中将默认方法实现添加到接口。这将极大地模糊接口和抽象类之间的区别。有关详细信息,请参阅this video

答案 3 :(得分:2)

这两种机制存在的一个重要原因是因为c#.NET只允许单继承,而不是像C ++那样的多重继承。类继承允许您只从一个地方继承实现;其他一切都必须通过实现接口来完成。

例如,假设我创建了一个类,如Car和I子类,分为三个子类:RearWheelDrive,FrontWheelDrive和AllWheelDrive。现在我决定我需要沿着不同的“轴”切割我的类,就像那些带有按钮启动器和没有按钮的那些。我希望所有按钮启动汽车都有一个“PushStartButton()”方法和非按钮汽车有一个“TurnKey()”方法,我希望能够处理汽车对象(关于启动它们),无论哪个子类他们是。我可以定义我的类可以实现的接口,例如IPushButtonStart和IKeyedIgnition,所以我有一种常见的方法来处理我的对象,这些对象的方式与每个派生的单个基类无关。

答案 4 :(得分:1)

你已经给出了一个很好的答案。我认为你的第二个答案是真正的原因。如果我想创建一个对象Compareable,我不应该从Comparable基类派生。如果您认为所有接口都考虑了您想要处理IComparable等基本接口的所有排列。

接口让我们围绕对象提供的公开暴露行为定义合同。抽象类允许您定义行为和实现,这是一个非常不同的事情。

答案 5 :(得分:1)

存在接口以提供没有任何实现的类,因此.NET可以在托管环境中提供安全和功能性多继承的支持。

答案 6 :(得分:0)

接口定义了实现类必须满足的契约;这是一种表明“这样做”的方式。抽象类是类的部分实现,根据定义,它是不完整的,需要完成衍生。他们是非常不同的东西。

答案 7 :(得分:0)

abstract class可以有实现,而interface只允许您创建实施者必须遵循的合同。使用抽象类,您可以为其子类提供常见的行为,而不能使用接口。

答案 8 :(得分:0)

它们有两个截然不同的目的。

抽象类提供了一种方法,使对象从定义的契约继承,并允许在基类中指定行为。从理论的角度来看,这提供了一个 IS-A 关系,因为具体类是基类的特定类型的IS-A。

接口允许类定义它们将实现的(或多个)合同。它们允许 ACTS-AS 或“可以用作”类型的关系,而不是直接继承。这就是为什么通常接口会使用形容词,因为它们是名称(IDisposable)而不是名词。

答案 9 :(得分:0)

接口用于类可以执行的操作,但它也用于隐藏类可以执行的操作。

例如,IEnumerable<T>接口描述了一个类可以遍历它的成员,但它也限制了对这个单一异能的访问。 List<T>也可以通过索引访问项目,但是当您通过IEnumerable<T>界面访问它时,您只知道它能够迭代成员。

如果方法接受IEnumerable<T>接口作为参数,则意味着它只对迭代成员的能力感兴趣。您可以使用具有此功能的多个不同类(如List<T>或数组T[]),而无需为每个类使用一种方法。

一个方法不仅可以接受几个实现接口的不同类,还可以创建实现接口的新类,方法也很乐意接受它们。

答案 10 :(得分:0)

这个想法很简单 - 如果你的类(YourClass)已经从父类派生(SomeParentClass),同时你希望你的类(YourClass)具有在某个抽象类中定义的新行为(SomeAbstractClass) ),你不能通过简单地从该抽象类派生(SomeAbstractClass)来做到这一点,C#不允许多重继承。 但是,如果您的新行为是在接口(IYourInterface)中定义的,则可以从接口(IYourInterface)和父类(SomeParentClass)轻松派生。

考虑使用由两个孩子( Apple &amp; Banana )派生的 Fruit 类,如下所示:

class Fruit
{
    public virtual string GetColor()
    {
        return string.Empty;
    }
}

class Apple : Fruit
{
    public override string GetColor()
    {
        return "Red";
    }
}

class Banana : Fruit
{
    public override string GetColor()
    {
        return "Yellow";
    }
}

我们在 C#中有一个现有界面 ICloneable 。该接口有一个如下所示的方法,实现此接口的类保证可以克隆它:

public interface ICloneable
{
    object Clone();
}

现在,如果我想让 Apple 类(不是 Banana 类)克隆,我可以像这样简单地实现 ICloneable

 class Apple : Fruit , ICloneable
{
    public object Clone()
    {
        // add your code here
    }

    public override string GetColor()
    {
        return "Red";
    }
}

现在考虑你的纯抽象类的论点,如果 C#有一个纯粹的抽象类,请说 Clonable 而不是接口 IClonable ,如下所示:< / p>

abstract class Clonable
{
    public abstract object Clone();
}

您现在可以通过继承抽象可克隆而不是 IClonable 来使您的 Apple 类能够克隆吗?像这样:

// Error: Class 'Apple' cannot have multiple base classes: 'Fruit' & 'Clonable'
class Apple : Fruit, Clonable
{
    public object Clone()
    {
        // add your code here
    }

    public override string GetColor()
    {
        return "Red";
    }
}

不,你不能,因为一个类不能从多个类派生。