在 C# 中使用接口有哪些优点?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1035632/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 06:38:43  来源:igfitidea点击:

What are some advantages to using an interface in C#?

c#oopinterfacedesign-patterns

提问by Kevin

I was forced into a software project at work a few years ago, and was forced to learn C# quickly. My programming background is weak (Classic ASP).

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

I've learned quite a bit over the years, but due to the forced nature of how I learned C#, there are a lot of basic concepts I am unclear on.

这些年来我学到了很多东西,但是由于我学习 C# 的方式的强制性质,有很多我不清楚的基本概念。

Specifically, an interface. I understand the basics, but when writing an app, I'm having a hard time figuring out a practical use of one. Why would one want to write an interface for their application?

具体来说,一个接口。我了解基础知识,但是在编写应用程序时,我很难弄清楚应用程序的实际用途。为什么要为他们的应用程序编写一个接口?

Thanks Kevin

谢谢凯文

采纳答案by Andrew Siemer

An interface says how something should work. Think of it as a contract or a template. It is key to things such as Inverson of Control or Dependancy Injection.

一个接口说明了某些东西应该如何工作。将其视为合同或模板。它是诸如控制反转或依赖注入之类的关键。

I use Structure Map as my IoC container. This allows me to define an interface for all of my classes. Where you might say

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

Widget w = new Widget();

I would say

我会说

IWidget w = ObjectFactory.GetInstance<IWidget>();

This is very powerful in that my code isn't saying necessarily what a Widget truely is. It just knows what a Widget can do based on the interface of IWidget.

这非常强大,因为我的代码并不一定说明 Widget 真正是什么。它只是基于IWidget 的接口知道Widget 可以做什么。

This has some great power to it in that now that I am using an IoC container I can do a couple more nifty things. In my unit tests where I need to use a Widget I can create a mock for Widget. So say that my Widget does something very powerful by way of connecting to a database or a web service, my mock can simulate connecting to these resources and return to me stubbed data. This makes my test run faster and behave in a way that is more reliable. Because I am using StructureMap I can tell StructureMap to load the real implementation of my Widget during production use of my code and the mocked version of the Widget during testing either programatically or by configuration.

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

Also, because I am using an IoC container I can provide cool new features to my application such as writing three different ways to cache data. I can have a local developer box cache using a tool such as Lucene.NET for a local cache. I can have a development server use the .NET cache which runs great on one box. And then I can have a third option for my production servers use a cache layer such as MemCache Win32 or Velocity. As long as all three caching implementations conform to the same interface, their actual implementation doesn't concern me (or my code) at all. I simply ask StructureMap to go get the current environments implementation and then go to work.

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

If you follow Dependency Injection at all then interfaces come in handy here also with an IoC container such as StructureMap in that I can declare the usage of a class by way of an Interface in the constructor of my class.

如果您完全遵循依赖注入,那么接口在这里也可以使用 IoC 容器(例如 StructureMap)派上用场,因为我可以通过类的构造函数中的接口来声明类的用法。

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

And then when I new up an instance of Widget by way of StructureMap such as this

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

IWidget widget = ObjectFactory.GetInstance<IWidget>();

Notice that I am not specifying the repository or service in the constructor. StructureMap knows by way of the interfaces specified in the constructor how to go get the appropriate instances and pass them in too. This makes very powerful and clean code!

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

All from the simple definition of Interfaces and some clever usage of them!

所有这些都来自接口的简单定义和它们的一些巧妙用法!

回答by Stuart

good article.

好文章。

An interface is a contract that guarantees to a client how a class or struct will behave.

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

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

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

This might be the clearest easiest way of explaining that I have come across:

这可能是我遇到的最清楚最简单的解释方法:

"The answer is that they provide a fairly type-safe means of building routines that accept objects when you don't know the specific type of object that will be passed ahead of time. The only thing you know about the objects that will be passed to your routine are that they have specific members that must be present for your routine to be able to work with that object. The best example I can give of the need for interfaces is in a team environment. Interfaces help define how different components talk to each other. By using an interface, you eliminate the possibility that a developer will misinterpret what members they must add to a type or how they will call another type that defines an interface. Without an interface, errors creep into the system and don't show up until runtime, when they are hard to find. With interfaces, errors in defining a type are caught immediately at compile time, where the cost is much less."

“答案是它们提供了一种相当类型安全的方法来构建接受对象的例程,当您不知道将提前传递的对象的特定类型时。您唯一知道将传递的对象对您的例程而言,它们具有特定的成员,这些成员必须存在于您的例程中才能使用该对象。我可以给出需要接口的最佳示例是在团队环境中。接口有助于定义不同组件如何通信彼此之间。通过使用接口,您可以消除开发人员误解他们必须添加到类型的成员或他们将如何调用定义接口的另一种类型的可能性。没有接口,错误会蔓延到系统中并且不会显示到运行时,当它们很难找到时。使用接口,定义类型的错误会在编译时立即捕获,成本要低得多。”

回答by kostia

The basic case is the "IWriter" case.

基本案例是“IWriter”案例。

Suppose you are making a class that can write to the console, and it has all kinds of useful functions like write() and peek().

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

Then you would like to write a class that can write to the printer, so instead of reinventing a new class, you use the IWriter interface.

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

Now the cool thing about interfaces is you can write all your writing code, without knowing what is your writing target beforehand, and then can when the user decides (at runtime) weather he wants to write to the console or the printer, you just define the object as a console/printer writer and you don't need to change anything in your writing code, because they both use the same front end (interface).

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

回答by Carl Manaster

Here is a bookthat talks all about interfaces. It promotes the notion that interfaces belong to the client, that is to say the caller. It's a nice notion. If you only need the thing that you're calling to implement - say - count() and get(), then you can define such an interface and let classes implement those functions. Some classes will have many other functions, but you're only interested in those two - so you need to know less about the classes you're working with. As long as they satisfy the contract, you can use them.

这是一关于接口的。它促进了接口属于客户端的概念,即调用者。这是个好主意。如果你只需要你调用的东西来实现——比如——count() 和 get(),那么你可以定义这样一个接口并让类实现这些功能。某些类将具有许多其他功能,但您只对这两个功能感兴趣 - 因此您需要较少了解您正在使用的类。只要它们满足合同,您就可以使用它们。

回答by nos

An example. Consider an MDI application that shows reports, there's basically 2 different report types. A chart, and a grid. I need to Save these reports as PDF and I need to mail them to someone. The event handler for the menu the user clicks to save a report to PDF could do this:

一个例子。考虑一个显示报告的 MDI 应用程序,基本上有 2 种不同的报告类型。一个图表,一个网格。我需要将这些报告另存为 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();
   }
}

I'll rather make my ChartReport and GridReport implement this interface:

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

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

Now I can do:

现在我可以这样做:

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

Similar for other code that need to do the same operation(save it to a file,zoom in,print,etc.) on the different report types. The above code will still work fine when I add a PivotTableReport also impelmenting Rpoert the next week.

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

回答by johnc

IOC and Dependency injection have already been mentioned above, and I would urge you to look at them.

IOC和依赖注入已经在上面提到过,我强烈建议你看看它们。

Largely, however, interfaces allow a contract to be specified for an object that doesn't require an inheritance model.

然而,在很大程度上,接口允许为不需要继承模型的对象指定契约。

Lets say I have class Foo, that has functions x and y and property z, and I build my code around it.

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

If I discover a better way to do Foo, or another sort of Foo requires implementation, I can, of course, extend a base Foo class to FooA, FooB, MyFoo etc, however that would require that all Foos have the same core functionality, or, indeed that any future Foo creators have access to the base Foo class and understand its internal workings. In C#, that would mean future Foos could not inherit from anything else but Foo, as C# does not support multiple inheritance.

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

It would also require me to be aware of possible future states of Foo, and try not to inhibit them in my base Foo class.

它还要求我了解 Foo 未来可能的状态,并尽量不要在我的基本 Foo 类中抑制它们。

Using an interface IFoo simply states the 'contract' that a class requires to work in my Foo framework, and I don't care what any future Foo classes may inherit from or look like internally, as long as they have fn x fn y and z. It makes a framework much more flexible and open to future additions.

使用接口 IFoo 只是声明了一个类在我的 Foo 框架中工作所需的“契约”,我不在乎任何未来的 Foo 类可能从内部继承或看起来像什么,只要它们有 fn x fn y 和z。它使框架更加灵活,并对未来的添加开放。

If, however, Foo requires a large amount of core at its base to work that would not be applicable in a contract scenario, that is when you would favour inheritance.

但是,如果 Foo 在其基础上需要大量核心才能工作,而这在契约场景中不适用,那么您将倾向于继承。

回答by Larsenal

One Simple Answer:Use interfaces to program against the contractrather than the implementation.

一个简单的答案:使用接口针对契约而不是实现进行编程。

How could that possibly help?Starting to use interfaces will (hopefully) get you in the habit of coupling classes more loosely. When you code against your own concrete classes, it's easy to start poking the data structures without a strict separation of concerns. You end up with classes which "know" everything about the other classes and things can get pretty tangled. By limiting yourself to an interface, you only have the assurance that it fulfills the interface's contract. It injects a sometimes helpful friction against tight coupling.

这怎么可能有帮助?开始使用接口将(希望)让您养成更松散地耦合类的习惯。当您针对自己的具体类进行编码时,很容易在没有严格分离关注点的情况下开始探索数据结构。你最终得到的类“知道”其他类的一切,事情可能会变得非常混乱。通过将自己限制在一个接口上,您只能保证它满足接口的约定。它注入了有时对紧耦合有用的摩擦。

回答by Larsenal

Couple of things, when you inherit from an interface it forces you to implement all the methods defined in the interface. For another, this is also a good way to bring in multiple inheritance which is not supported for regular classes. http://msdn.microsoft.com/en-us/library/ms173156.aspx

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

回答by Rodrick Chapman

Simple answer based on first principles:

基于首要原则的简单答案:

A program is a universe with its own metaphysics (the reality/substance/stuff of the code) and epistemology (what you can know/believe/reason about the code). A good programming language tries to maximize the metaphysical flexibility (lets you make the stuff easily) while ensuring epistemic rigor (makes sure your universe is internally consistent).

程序是一个宇宙,拥有自己的形而上学(代码的现实/实体/内容)和认识论(您可以了解/相信/关于代码的原因)。一个好的编程语言试图最大限度地提高形而上学的灵活性(让你轻松制作东西),同时确保认知严谨(确保你的宇宙内部一致)。

So, think of implementation inheritance as a metaphysical building block (the stuff that makes up your little universe of code) and interface inheritance as an epistemic constraint (it allows you to believe something about your code).

因此,将实现继承视为形而上学的构建块(构成您的代码小宇宙的东西),将接口继承视为认知约束(它允许您相信有关代码的某些东西)。

You use interfaces when you only want to ensure that you can believe something. Most of the time that's all you need.

当您只想确保可以相信某些东西时,您可以使用接口。大多数时候,这就是你所需要的。

回答by Bluestone

You mentioned having difficulty finding a practical use for interfaces.. I've found that they come into their own when building extensible applications, for example a plugin-based app where a third-party plugin must conform to specific rules.. These rules can be defined by an interface.

你提到很难找到接口的实际用途。由接口定义。

You could make it so that when the plugin is loaded, it must have an Init method that takes a class that implements IServices interface.

您可以这样做,以便在加载插件时,它必须有一个 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
    }
}

So.. If you have a class that implements the IServices interface, and then you instantiate it once, you can pass it to all the plugins upon initialisation and they can use whatever services you have defined in the interface.

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