继承与C#中的接口
本文关键字:接口 继承 | 更新日期: 2023-09-27 17:58:07
可能的重复:
接口与基类
在设计C#类库时,什么时候应该选择继承而不是接口?
所以我正在用C#编写我的第一个真正的程序。该程序将从四个不同的网站收集数据。我的计划是有一个看起来像这样的父类:
class Scraper
{
string scrapeDate(url);
string scrapeTime(url);
//&c.
}
然后我将有四个从中继承的类。
另一种选择是使Scraper
成为一个接口,并有四个类来实现它
这些方法之间有什么区别?
类继承表示"is-a"关系,例如,Tank
是Vehicle
。如果您的情况不符合这一点,那么选择接口而不是继承。
如果所提出的基类没有为您的方法提供默认实现,那么这将是选择接口而不是继承的另一个原因。
在C#中,只能从一个类继承,但可以从多个接口继承。这将是选择接口而不是继承的另一个原因。
明白了吗?
在我看来,在可能的情况下,继承是更好的方法。在基类中实现通用特性有助于确保底层实现的一致性,而实现接口只能保证接口的一致性。任何可能的继承都是OOP三脚架的一条腿,并限制代码重复。
当我的对象不能或不应该有公共基类,但需要提供类似的功能时,我会使用接口。实现通用接口的类可能(也可能确实)暴露额外的功能,可能实现多个接口等。
例如:在一个应用程序中,我的数据访问层是围绕"提供者"类构建的,这些类将业务对象和数据库代理对象与数据库隔离开来。在一个例子中,我有一个与SQL Server数据库交互的提供者,另一个用于与Oracle CRM On Demand(又名Why God Why)通信。两者都实现了一个不可知的接口,这样客户端代码就不在乎它在处理哪个数据存储,也不在乎处理每个数据存储的怪癖。
Oracle提供程序通过一组web服务与托管服务器通信,管理连接会话、批处理请求等。SQL提供程序使用一组存储过程。尽管两个接口都接受相同的数据类,但Oracle提供程序会转换数据以匹配其深奥的(说得轻一点)模式。有了这个实现,我可以很容易地添加一个提供者来使用XML数据存储、不同的RDBMS或stub/mock单元测试。
在这种情况下,这些东西有一个公共基类没有多大意义,在少数情况下,这是不可能的。
老实说,两者兼而有之。
public interface IScraper
{
string ScrapeDate(string url);
}
public abstract class Scraper : IScraper
{
public string ScrapeDate(string url)
{
// default implementation
}
}
无论哪种方式都有优势,但如果不了解更多的需求,这些优势很难量化。然而,没有理由两者都不能。为您的类提供一个接口使它也可以用于测试目的。
不过,还有其他需要考虑的问题;如果每个派生类的功能足够相似,那么简单地拥有一个将参数带到构造函数的类可能会更容易。
引用抽象类与接口。
有一些相似之处接口和一个抽象类:
一个类可以实现几个接口
一个类只能继承一个抽象班接口不能提供任何代码,只是签名
抽象类可以提供完整、默认代码和/或必须重写的详细信息。接口无法访问子、函数的修饰符,属性等一切都假定公开
抽象类可以包含访问子、函数的修饰符,属性接口用于定义一个阶级的外围能力。在里面换句话说,人和车辆都可以从IMovable接口继承
抽象类定义核心一个类的身份用于相同类型的对象。如果各种实现仅共享方法签名,则最好使用接口
如果不同实现是同类的并使用常见的行为或状态那么最好使用抽象类。如果我们向接口添加一个新方法然后我们必须追踪所有接口和定义新的实现方法
如果我们向抽象类,那么我们可以选择提供默认实现因此所有现有的代码可能正常工作。接口中不能定义任何字段
抽象类可以有字段和约束定义
接口只包含方法、委托或事件的签名。方法的实现是在实现接口的类中完成的。
一个类可以实现多个接口。
一个类只能有一个直接基类。
接口允许定义常见行为的结构。
如果您可以提取一个或多个特定行为的通用实现,那么继承就很有用。
基本上,如果几个类以相同的方式抓取日期,那么将scrapeDate放在基类中是有意义的;否则,只使用一个接口,并在实现接口的每个类中定义特定的scrapeDate。
如果您有通用功能,您应该使用继承-该功能将在所有子类中可用,每个子类都可以扩展或覆盖父类代码。
如果您的类中有一些内容,您将使用接口来确保所有类实现相同的方法和属性,但不一定实现相同的功能。
主要区别:
- 接口根本没有实现,而抽象基类可以实现通用功能
- 一个类只能从一个基类继承,但它可以实现多个接口
在您的情况下,可能所有的scraper类都需要一些通用特性,因此让它们都继承自一个通用基类
是有意义的。如果你想要包含一些公共逻辑或一些公共数据成员,那么你可以使用基类并从中继承。你所做的是要求每个子级实现一组最小的逻辑。