在C#中使用接口有什么好处?

几年前我被迫进入一个工作的软件项目,并被迫快速学习C#。 我的编程背景很弱(经典ASP)。

这些年来我学到了很多东西,但由于我学习C#的强制性,我还有很多基本概念。

具体来说,一个界面。 我理解基础知识,但在编写应用程序时,我很难找到实际使用的应用程序。 为什么要为他们的应用程序编写接口?

谢谢凯文

界面说明了某些东西应该如何运作。 将其视为合同或模板。 它是Inverson of Control或Dependancy Injection等关键。

我使用Structure Map作为我的IoC容器。 这允许我为我的所有类定义一个接口。 你可能会说的

Widget w = new Widget(); 

我会说

 IWidget w = ObjectFactory.GetInstance(); 

这是非常强大的,因为我的代码并不一定说Widget确实是什么。 它只知道Widget可以基于IWidget的界面做什么。

由于我使用的是IoC容器,我可以做一些非常好的事情。 在我需要使用Widget的unit testing中,我可以为Widget创建一个模拟器。 所以说我的Widget通过连接到数据库或Web服务来做一些非常强大的function,我的模拟可以模拟连接到这些资源并返回给我的存根数据。 这使我的测试运行得更快,并且行为更加可靠。 因为我正在使用StructureMap,所以我可以告诉StructureMap在我的代码的生产使用期间加载我的Widget的实际实现,并且在测试期间以编程方式或通过配置来加载Widget的模拟版本。

另外,因为我使用的是IoC容器,所以我可以为我的应用程序提供很酷的新function,例如编写三种不同的缓存数据的方法。 我可以使用Lucene.NET等工具为本地缓存创建一个本地开发人员框缓存。 我可以让开发服务器使用.NET缓存,它在一个盒子上运行很好。 然后,我可以为生产服务器提供第三个选项,使用缓存层,如MemCache Win32或Velocity。 只要所有三个缓存实现都符合相同的接口,它们的实际实现根本不涉及我(或我的代码)。 我只是要求StructureMap获取当前的环境实现,然后开始工作。

如果您完全遵循dependency injection,那么接口在这里也可以使用诸如StructureMap之类的IoC容器,因为我可以在我的类的构造函数中通过接口声明类的用法。

 public class Widget(IWidgetRepository repository, IWidgetService service) : IWidget { //do something here using my repository and service } 

然后当我通过StructureMap这样新建一个Widget实例时

 IWidget widget = ObjectFactory.GetInstance(); 

请注意,我没有在构造函数中指定存储库或服务。 StructureMap通过构造函数中指定的接口知道如何获取适当的实例并将其传递给它们。 这使得代码非常强大和干净!

所有这些都来自Interfaces的简单定义以及它们的一些巧妙用法!

基本情况是“IWriter”案例。

假设您正在创建一个可以写入控制台的类,它具有各种有用的函数,如write()和peek()。

然后,您想编写一个可以写入打印机的类,因此您不必重新创建新类,而是使用IWriter接口。

现在关于接口的很酷的事情是你可以编写所有的编写代码,而不必事先知道你的写入目标是什么,然后当用户决定(在运行时)天气他想要写入控制台或打印机时,你只需要定义作为控制台/打印机编写器的对象,您不需要更改编写代码中的任何内容,因为它们都使用相同的前端(接口)。

一个简单的答案:使用接口来编写合同而不是实现

怎么可能有帮助? 开始使用接口将(希望)让你习惯于更松散地耦合类。 当您针对自己的具体类进行编码时,很容易开始调用数据结构而不需要严格区分关注点。 你最终得到的课程“知道”其他课程的一切,事情可能变得非常混乱。 通过将自己限制为接口,您只能确保它满足接口的合同。 它注入有时有助于紧密耦合的摩擦力。

一个例子。 考虑一个显示报告的MDI应用程序,基本上有两种不同的报告类型。 图表和网格。 我需要将这些报告保存为PDF,我需要将它们邮寄给某人。 用户单击以将报告保存为PDF的菜单的事件处理程序可以执行以下操作:

 void ExportPDF_Clicked(...) { if(currentDocument is ChartReport) { ChartReport r = currentDocument as ChartReport; r.SavePDF(); } else if(currentDocument is GridReport) { GridReport r = currentDocument as GridReport; r.SavePDF(); } } 

我宁愿让我的ChartReport和GridReport实现这个接口:

 public interface Report { void MailTo(); void SavePDF(); } 

现在我可以做:

 void ExportPDF_Clicked(...) { Report r = currentDocument as Report; r.SavePDF(); } 

类似于需要在不同报告类型上执行相同操作(将其保存到文件,放大,打印等)的其他代码。 当我在下周添加PivotTableReport同样激励Rpoert时,上面的代码仍然可以正常工作。

上面已经提到过IOC和dependency injection,我建议你看看它们。

但是,很大程度上,接口允许为不需要inheritance模型的对象指定合同。

假设我有类Foo,它有函数x和y以及属性z,我在它周围构建我的代码。

如果我发现一个更好的方法来做Foo,或者另一种Foo需要实现,我当然可以将基础Foo类扩展到FooA,FooB,MyFoo等,但这需要所有Foo具有相同的核心function,或者,确实任何未来的Foo创建者都可以访问基础Foo类并了解其内部工作原理。 在C#中,这意味着未来的Foos除了Foo之外不能inheritance其他任何东西,因为C#不支持多重inheritance。

它还需要我了解Foo可能的未来状态,并尽量不要在我的基础Foo类中禁止它们。

使用接口IFoo简单地说明了一个类在我的Foo框架中工作所需的“契约”,并且我不关心任何未来的Foo类可以从内部inheritance或看起来像内部,只要它们具有fn x fn y和ž。 它使框架更加灵活,对未来的添加开放。

但是,如果Foo需要在其基础上运行大量的核心,而这种核心在合同场景中不适用,那就是您希望inheritance。

这是一本讲述所有接口的书 。 它促进了接口属于客户端的概念,即调用者。 这是一个很好的概念。 如果你只需要你要调用的东西 – 比如 – count()和get(),那么你可以定义这样一个接口,让类实现这些函数。 有些类会有许多其他function,但是你只对这两个function感兴趣 – 所以你需要更少地了解你正在使用的类。 只要他们满足合同,您就可以使用它们。

好文章。

接口是一种合同,它向客户端保证类或结构的行为方式。

http://www.codeguru.com/csharp/csharp/cs_syntax/interfaces/article.php/c7563

这可能是解释我遇到过的最简单明了的方法:

“答案是,当你不知道将提前传递的特定对象类型时,它们提供了一种相当类型安全的方法来构建接受对象的例程。你唯一知道将要传递的对象你的日常工作就是他们必须有特定的成员才能使你的例程能够使用该对象。我可以给出接口需求的最好例子是在团队环境中。接口有助于定义不同组件如何对话通过使用接口,您可以消除开发人员误解他们必须添加到某个类型的成员或者如何调用另一个定义接口的类型的可能性。没有接口,错误会蔓延到系统中而不会直到运行时才显示它们很难找到。有了接口,定义类型的错误会在编译时立即被捕获,而成本则要低得多。“

有些事情,当您从接口inheritance时,它会强制您实现接口中定义的所有方法。 另一方面,这也是引入常规类不支持的多重inheritance的好方法。 http://msdn.microsoft.com/en-us/library/ms173156.aspx

基于第一原则的简单答案:

程序是一个拥有自己的形而上学(代码的现实/物质/东西)和认识论(你可以知道/相信/推理代码)的宇宙。 一种好的编程语言试图最大化形而上学的灵活性(让你轻松地制作东西),同时确保认知的严谨性(确保你的宇宙在内部是一致的)。

因此,将实现inheritance视为一个形而上学的构建块(构成您的小代码世界的东西)和接口inheritance作为一种认知约束(它允许您相信您的代码)。

当您只想确保相信某些内容时,可以使用接口。 大部分时间都是你需要的。

您提到很难找到接口的实际用途。我发现在构建可扩展应用程序时它们会自成一体,例如基于插件的应用程序,其中第三方插件必须符合特定规则。这些规则可以由接口定义。

你可以这样做,以便在加载插件时,它必须有一个Init方法,它接受一个实现IServices接口的类。

 public interface IServices { DataManager Data { get; set; } LogManager Log { get; set; } SomeOtherManager SomeOther { get; set; } } public class MrPlugin { public void Init(IServices services) { // Do stuff with services } } 

所以..如果你有一个实现IServices接口的类,然后你实例化它一次,你可以在初始化时将它传递给所有的插件,他们可以使用你在界面中定义的任何服务。