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

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

是因为:

  1. 接口存在多重inheritance
  2. 有接口是有意义的,因为对象的CAN-DOfunction应放在接口而不是基本抽象类中。

请澄清

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

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

就个人而言,我并没有因为inheritance而在整个“is-a”vs“can-do”区别。 它从来没有给我一个关于做什么的直觉,只是玩弄不同的想法,看看哪些人觉得最灵活。 (再说一遍,我非常喜欢“对inheritance的偏爱”……)

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

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

派生自Derived类不能再覆盖Foo

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

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

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

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

所以,如果我有一个SourceFile类,那么我的类可以选择实现ISourceControl ,它说’我忠实地承诺实现ISourceControl所需的方法和属性’

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

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

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

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

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

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

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

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

存在接口以提供没有任何实现的类,因此.NET可以在托管环境中提供对安全和function多重inheritance的支持。

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

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

它们有两个截然不同的用途。

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

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

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

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

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

方法不仅可以接受实现接口的几个不同的类,还可以创建实现接口的新类,并且方法也会愉快地接受它们。

考虑使用由两个孩子( AppleBanana )派生的类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#中有一个现有的IClone接口。 此接口具有如下所示的单个方法,实现此接口的类保证可以克隆它:

 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,如下所示:

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

您现在可以通过inheritance抽象Clonable而不是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"; } } 

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