如果我有一个包含类似类的项目,有些可能使用相同的实现,但在大多数情况下,它们实现了自己处理接口或抽象类中定义的方法的方式。我试图弄清楚接口/抽象类是否更好。如果你可以使用虚拟抽象方法的抽象类,我就不明白界面了。
这是一个界面:
public interface IAthlete
{
void Run();
}
这是一个抽象类:
public abstract class Athlete
{
public abstract void Run();
}
以下是界面的实现:
public class Sprinter : IAthlete
{
public void Run()
{
Console.WriteLine("Running Fast....");
}
}
以下是抽象类的扩展:
public class MarathonRunner : Athlete
{
public override void Run()
{
Console.Write("Jogging....");
}
}
现在,如果我决定向接口或抽象方法添加一个名为Stop的方法,Sprinter和MarathonRunner都会中断,因为我可以提供一些默认的抽象实现,这似乎是一个更好的选择。我错过了什么吗?
答案 0 :(得分:6)
接口和抽象超类之间有两个主要区别:
抽象类
接口
答案 1 :(得分:1)
如果您所拥有的只是一个提取的共性,那么两者之间没有实质性区别是完全正确的。但这更像是说“在将1
添加到2
的情况下,int
和double
之间没有区别” - 它的技术上是真的< / em>,但不是一个特别有用的如何思考的指南。
如果比这更复杂(在大多数情况下),将会有更多的类,以及要提取的常见数据块。然后你必须开始在类继承和接口实现之间做出重要的选择,同时考虑以下内容:
等等。
一般来说,你的课程的语义应该指导你 - “事物”有“IS A”关系(比如MarathonRunner to Athlete),建议继承; “事物”中有“我能完成A的合同”(比如说,Run to Runner),建议使用界面实现。
答案 2 :(得分:0)
接口是一个非常有用的功能,与抽象类非常相似,在某些情况下,可以通过抽象类进行交换。< / p>
但是,不要直接跳到接口,取消你必须(在Java开发人员中非常常见的反模式)。我建议,通过阅读你的例子,坚持抽象类。
大多数时候我只使用接口,当我有几个非相关类时,我需要它们有共同的成员,如果这些类来自同一个基类。
在您的示例中,您在尝试查找在添加基本虚拟方法时如果需要新的stop
方法会发生什么。这些可以用不同的方法解决,不是抽象类与接口。
有3种选择:
(1)添加一个抽象方法,强制程序员覆盖它,以便实例化对象。
(2)添加一个新的虚拟方法,该方法可以执行某些操作,但不必重写。
(3)添加一个什么都不做的新方法,可能适用于你的情况。
// cannot instantiate an abstract class
public abstract class Athlete
{
// helper method:
public /* non-abstract */ void DoNothing()
{
// does nothing on purpouse !!!
}
// (1) virtual & abstract method, must be overriden
public abstract void Run();
// (2) new virtual method, doesn't need to be overriden,
// but, maybe you dont like what it does
public virtual void Stop()
{
Message.Show("Stop !!!");
}
// (3) new virtual method, doesn't need to be overriden,
// its safe to be called
public virtual void TakeBreak()
{
// works like an abstract virtual method, but, you don't need to override
DoNothing();
}
} // class Athlete
// in a non abstract class, you must override all abstract methods
public /* non-abstract */ class Runner: Athlete
{
public override void Run()
{
DoNothing();
}
public override void Stop()
{
DoNothing();
}
// don't need to override this method
// public virtual void TakeBreak();
} // class Trekker
// ...
Runner ARunner = new Runner();
ARunner.Run();
ARunner.Stop();
ARunner.TakeBreak();
可能适用于您的示例的第三种虚方法并没有特殊的名称,我已经在stackoverflow上发布了一个关于它的问题,但是,没有人知道这个案例的特殊名称。
干杯。
答案 3 :(得分:0)
接口和抽象类之间的一个重要区别是它们的成员如何处理多代继承。假设有抽象方法BaseFoo
的抽象类Bar
和方法IFoo
的接口Boz
; class Foo
继承BaseFoo
并实现IFoo
,而class DerivedFoo
继承自Foo
。
如果DerivedFoo
需要覆盖BaseFoo.Bar
,则可能会覆盖base.Bar()
,如果需要使用其父实施,则覆盖可能会调用Foo
。如果Boz
使用虚方法隐式实现DerivedFoo
,则base.Boz()
可以覆盖该方法并调用Foo
(覆盖是类的函数而不是接口)但是如果IFoo.Boz
明确实现了DerivedFoo
,那么IFoo.Boz
改变Foo
行为的唯一方法就是重新实现它。如果是这样,那么IFoo.Boz
DerivedFoo
的实施将无法访问,即使在{{1}}的同一接口成员的实施中也是如此。
答案 4 :(得分:-1)
接口是一个很好的方式,因为.NET开发人员当前的共识是,你应该支持组合而不是继承,因此接口是一个更好的策略(想想注入容器以及它们的用途,以及让我们不要开始单元测试)。 类,类可以实现许多接口,但只能从一个类(抽象或其他)继承。结构体也可以实现接口,但不能从其他类继承。 在运行时级别,接口更有效,因为运行时不必遍历继承堆栈以计算调用特定成员的多态含义。