抽象类与接口
我对C#中抽象类的使用有点困惑。 在C ++中,定义一个模板inheritance了抽象类可以遵循的是有意义的。 但是,在C#不接口服务于同一目的?
真的,抽象类可以有默认的实现,而不是由接口提供。 因此,如果实现不需要包含在基类中,那么最好使用接口?
我仍然喜欢提供一个接口的默认抽象实现,假设它是一个实际的接口(它是有道理的)。 你永远不知道什么时候可以向界面添加一些东西,这个界面有一个简单的默认实现,可以被“免费”包含,并给予从抽象基类inheritance的任何人。
这个CodeProject文章有很多关于两者之间的差异的信息,包括比较和对比每个特征的表。
接口定义了类之间的契约 – 类之间相互呼叫的方式。 一个类可以实现多个接口,但只能从一个抽象类inheritance。
真的,抽象类可以有一个接口不提供的默认实现。 因此,如果实现不需要包含在基类中,那么最好使用接口?
是的:)。 如果在基类中实现一些方法是有意义的,这些方法对于所有被破坏的类都是通用的, 那么你应该使用一个抽象类。 如果基类只用于定义接口,但在inheritance的类之间没有公共逻辑,请使用接口。
对于你的第一个问题,是的。
为了您的第二个答案,我会给你一些我已经遵循的提示。
- 结合使用抽象类和接口来优化您的devise权衡。
使用抽象类
-
当创build一个广泛分布或重用的类库时,特别是对于客户端,使用抽象类优先于接口; 因为它简化了版本控制。
-
使用抽象类来定义一个types族的公共基类。
-
使用抽象类来提供默认行为。
-
仅子类逻辑上属于的层次结构中的基类。
使用一个接口
-
当创build一个可以随意更改的独立项目时,优先使用一个接口来抽象类; 因为它提供了更多的devise灵活性。
-
使用接口来引入多态行为而不需要子类化,并为多重inheritancebuild模 – 允许特定types支持多种行为。
-
使用接口为值typesdevise多态层次结构。
-
当一个不可变的契约是真正意图的时候使用一个接口。
-
精心devise的界面定义了一个非常具体的function范围。 拆分包含不相关function的接口。
您可以实现任意数量的接口,但只能inheritance一个Class。 所以类和接口在C#中是完全不同的,你不能交替使用它们。 在C#中抽象类仍然是类,而不是接口。
接口和抽象类服务于不同的目标。 接口用于声明类的契约,而抽象类用于共享一个通用的实现。
如果你只使用抽象类,你的类不能inheritance其他类,因为C#不支持多重inheritance。 如果你只使用接口,你的类不能共享通用的代码。
public interface IFoo { void Bar(); } public abstract class FooBase : IFoo { public abstract void Bar() { // Do some stuff usually required for IFoo. } }
现在我们可以在各种情况下使用接口和基础实现。
public class FooOne : FooBase { public override void Bar() { base.Bar(); // Use base implementation. // Do specialized stuff. } } public class FooTwo : FooBase { public override void Bar() { // Do other specialized stuff. base.Bar(); // Use base implementation. // Do more specialized stuff. } } // This class cannot use the base implementation from FooBase because // of inheriting from OtherClass but it can still implement IFoo. public class FooThree : OtherClass, IFoo { public virtual void Bar() { // Do stuff. } }
如果你没有任何默认/公共代码,那么去一个界面。
一个抽象类也可以作为一个模板,它定义了一些algorithm的步骤和它们被调用的顺序,派生类提供了这些步骤的实现:
public abstract class Processor { // this is the only public method // implements the order of the separate steps public void Process() { Step1(); Step2(); //... } // implementation is provided by derived classes protected abstract void Step1(); protected abstract void Step2(); }
虽然没有实现的抽象类与接口是等价的,但接口和抽象类被用于不同的事情。
接口可以用于最一般意义上的多态。 例如, ICollection
用于定义所有集合的接口(有很多)。 这里是定义你想要在某种types上执行的操作。 还有很多其他用途(如可testing性,dependency injection等)。 而且,接口可以混合使用,这在概念上和技术上都是可行的。
抽象类更多的是模板行为,虚拟方法是“填补空白”的地方。 显然你不能混用抽象类(至less不是C#)。
在C#中,使用抽象类的一个很大的威慑力就是你只能使用一个。 通过接口,您可以不限制实现的基类。 为此,我总是使用一个接口,即使我创build一个抽象基类来协助实现。
基本抽象类的另一个烦恼往往是依赖于模板参数。 这可能会使其余的代码非常难以使用。 简单的答案是提供一个接口来与抽象类交谈,而不需要知道模板类的types参数。
其他人似乎更快地input他们的答案,但让我总结一下…
使用一个接口。 如果您需要共享实现,您还可以创build一个提供常用实现细节的抽象基类。
请注意,使用C#3,您可以通过使用扩展方法为接口提供默认行为。 虽然有一些限制,抽象类仍然占有一席之地。
我build模时遵循的规则是:类(包含抽象)和结构模型实体。接口模型的行为。 实现接口的实体可以被看作是接口(合同)公开的展示行为。
这在一些答案中暗示,但没有明确说明。
事实上,你可以实现多个接口,只能从一个基类inheritance,就好像它们是同一枚硬币的两面,不是一个好的方法来看待它。
不要将接口看作对象层次结构的一部分。 他们通常只是function的一小部分(或者至less是特定的,如果不是很小的话),你的真实对象层次可以声明为实现。 以IDisposable为例。 如果你是那个写这个的人,你会问自己应该是一个抽象类还是一个接口? 很明显,在这种情况下,他们是两个完全不同的东西。 我想要一次性。 认为ICloneable和IEnumerable。 您可以在您的类中实现这些类,而无需尝试使类从List或Array等不相关的类派生。 或者拿IEnumerator。 只需将MoveNexttypes的视图赋予一个对象。 我的类可以提供该function,而不必笨拙地从一些其他顺序收集数据types派生,这与我的类无关。
只要基类没有一些真正的“重载”实现,我会一直比较喜欢接口,这将为实现者节省大量的时间。 给.net只允许一个基类的inheritance,迫使你的用户inheritance是一个巨大的局限性。
你应该总是喜欢编程接口,而不是具体的类。
如果你还想要一个默认的实现,你仍然可以创build一个实现你的接口的基类。