Using Interface variables
我仍在努力更好地理解接口。我知道它们是什么,以及如何在类中实现它们。
我不明白的是,当您创建一个属于您的接口类型的变量时:
1 | IMyInterface somevariable; |
你为什么要这样做?我不理解imyinterface如何像类一样使用……例如调用方法,因此:
1 | somevariable.CallSomeMethod(); |
为什么要使用imyinterface变量来执行此操作?
您没有创建接口的实例-您正在创建实现接口的某个实例。
接口的要点是,它保证任何时候实现它的东西都将提供它内部声明的方法。
因此,现在,使用您的示例,您可以:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | MyNiftyClass : IMyInterface { public void CallSomeMethod() { //Do something nifty } } MyOddClass : IMyInterface { public void CallSomeMethod() { //Do something odd } } |
现在你有了:
调用CallsomeMethod方法现在可以做一些漂亮的事情,也可以做一些奇怪的事情,当您使用imyinterface作为类型传入时,这变得特别有用。
1 2 3 4 | public void ThisMethodShowsHowItWorks(IMyInterface someObject) { someObject.CallSomeMethod(); } |
现在,根据您是用一个漂亮的类还是一个奇怪的类调用上面的方法,您会得到不同的行为。
1 2 3 4 5 6 7 8 9 10 11 12 |
编辑
这解决了我认为您的预期问题——为什么要将变量声明为接口类型?
这就是为什么要使用:
1 |
优先考虑:
1 |
希望在声明方法签名时使用接口的原因很清楚,但这就留下了关于局部作用域变量的问题:
1 2 3 4 5 6 7 8 |
通常没有技术上的原因来选择接口。我通常使用接口是因为:
- 我通常注入依赖项,因此需要多态性
- 使用接口清楚地说明了我只使用接口成员的意图
技术上需要接口的地方之一是使用多态性的地方,比如使用工厂创建变量,或者(如我上面所说)使用依赖注入。
借用伊托尔森的一个例子,使用具体的声明,您不能这样做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
因为:
1 2 | public void ReadItemsList(List<string> items); public void ReadItemsArray(string[] items); |
可以变成这样:
1 | public void ReadItems(IEnumerable<string> items); |
编辑
这样想:
You have to be able to do this.
而不是:
You have to be this.
本质上,这是方法与其调用方之间的契约。
我处于同样的位置,花了几天时间来弄明白为什么我们必须使用接口变量。
1 |
为什么不
1 |
设想一下,如果一个类实现两个接口,其中包含一个具有相同签名的成员,那么在该类上实现该成员将导致两个接口将该成员用作其实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Test { static void Main() { SampleClass sc = new SampleClass(); IControl ctrl = (IControl)sc; ISurface srfc = (ISurface)sc; // The following lines all call the same method. sc.Paint(); ctrl.Paint(); srfc.Paint(); } |
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | interface IControl { void Paint(); } interface ISurface { void Paint(); } class SampleClass : IControl, ISurface { // Both ISurface.Paint and IControl.Paint call this method. public void Paint() { Console.WriteLine("Paint method in SampleClass"); } |
}
1 2 3 4 | // Output: // Paint method in SampleClass // Paint method in SampleClass // Paint method in SampleClass |
但是,如果两个接口成员不执行相同的功能,这可能导致一个或两个接口的实现不正确。
1 2 3 4 5 6 7 8 9 10 11 | public class SampleClass : IControl, ISurface { void IControl.Paint() { System.Console.WriteLine("IControl.Paint"); } void ISurface.Paint() { System.Console.WriteLine("ISurface.Paint"); } } |
类成员IControl.paint仅通过IControl接口和ISurface可用。paint仅通过ISurface可用。两种方法实现都是独立的,并且都不能直接在类上使用。例如:
如果我错了,请纠正我,因为我还在学习这个接口概念。
使用接口变量是允许编写处理程序方法的唯一方法,该方法可以接受来自具有不同基类的对象的数据。
这几乎是任何人都能明白的。
假设你有船、车、卡车、飞机。
这些都共享一个公共方法takemethere(字符串目的地)
您将拥有一个接口:
1 2 3 4 | public interface ITransportation { public void TakeMeThere(string destination); } |
那么你的班级:
1 2 3 4 5 6 7 | public class Boat : ITransportation { public void TakeMeThere(string destination) // From ITransportation { Console.WriteLine("Going to" + destination); } } |
你在这里说的是,我的班轮也会按运输部告诉我的做。
当你想为运输公司制作软件的时候。你可以有一个方法
1 2 3 4 | Void ProvideServiceForClient(ITransportation transportationMethod, string whereTheyWantToGo) { transportationMethod.TakeMeThere(whereTheyWantToGo); // Cause ITransportation has this method } |
所以他们想要哪种交通方式并不重要,因为我们知道它可以带我去
我相信每个人都在回答使用接口的多态性原因,而DavidHall部分地谈到了为什么您将它作为接口而不是实际的对象名来引用。当然,仅限于接口成员等是有帮助的,但另一个答案是依赖注入/实例化。
当您设计应用程序时,如果您使用依赖注入,那么它通常更干净、更容易管理,并且更灵活。如果你从未做过,一开始感觉是倒退的,但当你开始回溯的时候,你会希望你做过。
依赖注入通常通过允许类实例化和控制依赖项来工作,而您只需要依赖所需对象的接口。
例子:
首先将应用程序分层。一级逻辑,二级接口,三级依赖注入。(每个人都有自己的方式,这只是为了展示)。
在逻辑层中,您引用接口和依赖层,然后最终仅基于外部对象的接口创建逻辑。
我们走到这里:
1 2 3 4 5 6 7 8 | public IEmployee GetEmployee(string id) { IEmployee emp = di.GetInstance<List<IEmployee>>().Where(e => e.Id == id).FirstOrDefault(); emp?.LastAccessTimeStamp = DateTime.Now; return emp; } |
注意上面我们如何使用di.getInstance从依赖项中获取对象。该层中的代码永远不会知道或关心Employee对象。事实上,如果它在其他代码中发生变化,在这里它将永远不会影响我们。如果IEemployee的接口发生更改,那么我们可能需要更改代码。
关键是,iemployee emp=永远不会真正知道实际对象是什么,但知道接口以及如何使用它。考虑到这一点,当您想要使用一个接口而不是一个对象时,因为我们永远不知道或无法访问该对象。
这是总结..希望有帮助。
不,这是不可能的。设计师没有提供一种方法。当然,这也是常识。因为接口只包含抽象方法,而且抽象方法没有(实现代码的)主体,所以我们无法创建对象。
假设即使它是允许的,它的用途是什么。用对象调用抽象方法不会产生任何目的,因为没有输出。抽象方法没有功能。那么,Java设计和编码中接口的使用是什么呢?它们可以用作原型,您可以从中轻松地开发新类。它们像其他类的模板一样工作,这些类实现接口,就像构建建筑的蓝图一样。
使用了一个接口,因此您不需要担心什么类实现了该接口。这一点很有用的一个例子是,当您有一个工厂方法返回一个具体的实现时,根据您运行的环境可能会有所不同。它还允许API设计者定义API,同时允许第三方以他们认为合适的任何方式实现API。Sun用Java的加密API来实现这一点。
1 2 3 4 5 6 7 8 9 10 11 |
使用工厂的代码只需要了解foo,而不需要了解任何特定的实现。
这不是特定于C,因此我建议移动到其他标志。关于你的问题,我们选择接口的主要原因是提供两个组件之间的协议(可以是dll、jar或任何其他组件)。请参阅以下内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | public class TestClass { static void Main() { IMyInterface ob1, obj2; ob1 = getIMyInterfaceObj(); obj2 = getIMyInterfaceObj(); Console.WriteLine(ob1.CallSomeMethod()); Console.WriteLine(obj2.CallSomeMethod()); Console.ReadLine(); } private static bool isfirstTime = true; private static IMyInterface getIMyInterfaceObj() { if (isfirstTime) { isfirstTime = false; return new ImplementingClass1(); } else { return new ImplementingClass2(); } } } public class ImplementingClass1 : IMyInterface { public ImplementingClass1() { } #region IMyInterface Members public bool CallSomeMethod() { return true; } #endregion } public class ImplementingClass2 : IMyInterface { public ImplementingClass2() { } #region IMyInterface Members public bool CallSomeMethod() { return false; } #endregion } public interface IMyInterface { bool CallSomeMethod(); } |
这里的主要方法不知道类的情况,但是它仍然能够使用接口获得不同的行为。
例如,您有两个类:
1 2 3 4 | public interface IReadable { public void Read(); } |
现在假设您正在编写一个应用程序,该应用程序将为用户阅读书籍和报纸。用户可以从列表中选择一本书或一份报纸,该项目将被用户阅读。
应用程序中读取给用户的方法将把这个
1 2 3 4 | public static void ReadItem(IReadable item) { item.Read(); } |
因为参数是一个
1 2 3 4 5 6 7 8 9 10 11 | public void Read() { this.Open(); this.TurnToPage(1); while(!this.AtLastPage) { ReadText(this.CurrentPage.Text); this.TurnPage(); } this.Close(); } |
1 2 3 4 5 6 7 8 9 10 | public void Read() { while(!this.OnBackPage) { foreach(Article article in this.CurrentPage.Articles) { ReadText(article.Text); } } } |
重点是,由接口类型的变量所包含的对象被保证具有一组特定的方法,即使对象的可能类没有以任何其他方式相关。这允许您编写应用于各种类的代码,这些类具有可以对其执行的公共操作。
接口的目的是定义多个对象之间的契约,独立于特定的实现。
因此,当您有一个内部
1 | class Something : ISomething |
因此,当您实例化一个契约时,将使用接口variable:
1 2 |
您还应该阅读接口C#
更新:
我将通过一个(真实的)例子来解释为变量而不是类本身使用接口的逻辑:
我有一个通用的库交互:
1 2 3 4 | Interface IRepository { void Create(); void Update(); } |
我有两个独立的实现:
1 2 | class RepositoryFile : interface IRepository {} class RepositoryDB : interface IRepository {} |
每个类都有一个完全不同的内部实现。
现在我有了另一个对象,一个日志记录程序,它使用一个已经建立的存储库来完成他的写作。这个对象不关心存储库是如何实现的,所以他只是实现:
1 | void WriteLog(string Log, IRepository oRep); |
顺便说一句,这也可以通过使用标准类继承来实现。但是使用接口和类继承之间的区别是另一个讨论。
有关抽象类和接口之间区别的更详细的讨论,请参见此处。
这是面向对象编程中的一个基本概念——多态性。(维基百科)
简短的回答是,通过使用类A中的接口,可以给类A提供
这也是松耦合的一种形式(维基百科),在那里你有许多类,但它们并不明确地相互依赖——只依赖于它们提供的一组属性和方法(接口)的抽象概念。