如果我们可以通过使类中的所有成员都是抽象的来实现接口功能,那么为什么抽象类和接口都存在于C#中。
是因为:
请澄清
答案 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
可以是抽象基本帐户,因为您可以拥有CheckingAccount
,SavingsAccount
等所有来自抽象基类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";
}
}
不,你不能,因为一个类不能从多个类派生。