接口和类之间的区别是什么?为什么可以在类中直接实现方法,为什么要使用接口?


117

我知道这是一个非常基本的问题,但是一个面试官以非常棘手的方式问我,我很无助:(

我只知道接口的材料或理论定义,并且在我从事的许多项目中也都实现了它。但是我真的不明白为什么这样做有用。

我也不了解界面中的一件事。即例如,我们使用

conn.Dispose();在最后块。但是我看不到该类正在实现或继承IDisposableinterface(SqlConnection)类。我想知道如何才能调用方法名称。同样,我也不了解Dispose方法的工作原理,因为我们需要为所有接口方法使用自己的实现来实现函数体。那么接口如何被接受或命名为合同呢?直到现在,这些问题一直在我脑海中浮现,坦率地说,我从未见过任何好的线索可以用我能理解的方式解释我的问题。

像往常一样,MSDN看起来非常可怕,而且没有清晰的界线(伙计们,请高水平开发的人为借口,我强烈认为任何代码或文章都应该引起任何人的注意,因此,就像许多其他人所说的那样,MSDN没用)。

面试官说:

他有5种方法,并且很乐意直接在类中实现它,但是如果您必须使用Abstract类或接口,则选择哪种方法,为什么?我确实回答了我在各种博客中读到的所有内容,并说了抽象类和接口的优缺点,但是他不相信,他是在总体上试图理解“为什么使用接口”。即使我只能一次实现相同的方法,也不能改变它,但一般来说,“为什么抽象类”还是如此。

我在网络上看不到任何地方,我可以得到一篇文章来清楚地向我解释接口及其功能。我是众多程序员之一,他们仍然不了解接口(我知道我使用的理论和方法),但不满意我清楚地了解它。


4
接口也是我也很难理解的一种。好问题。
布赖恩

4
简而言之,这意味着您可以在需要接口时替换实现接口的任何对象。
米奇·麦特

7
SqlConnection继承System.ComponentModel.Component哪个实现IDisposable
李李

2
@MitchWheat-这不是一个例子,问题问如何SqlConnection实现IDisposable
李李

哦,李,这让我明白了。但是我仍然看不到如何定义“处置”方法功能。
学习者2012年

Answers:


92

当您想创建类似的东西时,接口是非常好的:

using System;

namespace MyInterfaceExample
{
    public interface IMyLogInterface
    {
        //I want to have a specific method that I'll use in MyLogClass
        void WriteLog();       
    }

    public class MyClass : IMyLogInterface
    {

        public void WriteLog()
        {
            Console.Write("MyClass was Logged");
        }
    }

    public class MyOtherClass : IMyLogInterface
    {

        public void WriteLog()
        {
            Console.Write("MyOtherClass was Logged");
            Console.Write("And I Logged it different, than MyClass");
        }
    }

    public class MyLogClass
    {
        //I created a WriteLog method where I can pass as a parameter any object that implements IMyLogInterface.
        public static void WriteLog(IMyLogInterface myLogObject)
        {
            myLogObject.WriteLog(); //So I can use WriteLog here.
        }
    }

    public class MyMainClass
    {
        public void DoSomething()
        {
            MyClass aClass = new MyClass();
            MyOtherClass otherClass = new MyOtherClass();

            MyLogClass.WriteLog(aClass);//MyClass can log, and have his own implementation
            MyLogClass.WriteLog(otherClass); //As MyOtherClass also have his own implementation on how to log.
        }
    }
}

在我的示例中,我可以是一名编写MyLogClass人员的开发人员,其他开发人员可以创建其类,并且当他们想要登录时,他们可以实现接口IMyLogInterface。就像他们问我要使用中的WriteLog()方法需要实现什么一样MyLogClass。他们将在界面中找到答案。


3
嘿,对于我来说,这看起来像是一种很好的成分,我非常感谢,非常感谢:) :)
学习者2012年

14
我的问题是,如果要实例化MyClassMyOtherClass为什么不简单地调用aClass.WriteLog()为什么添加该额外步骤。WriteLog()每个类的实现将保持不同,但是您已经有了该对象,那么为什么将其传递给处理程序类呢?
Zach M.

嗯,也许是因为如果您将日志记录示例放在掘金上,那么其他人在不了解详细信息的情况下使用记录器会更简单..但另一方面,它仍然不是通用类,(例如,我可以编写一个具有日志记录和Allert级别的接口)接口仍仅在您的范围之内。因此,除了您自己之外,还有谁从中受益?
user3800527 '16

2
@ZachM。如果我是正确的,答案是,他不会实例化类,但是其他开发人员将实例化类并将其作为参数传递给MyLogClass WriteLog方法。因此,他的方法可以处理实现的任何对象IMyLogInterface是另一个有趣的帖子。
shaijut

1
我的问题是为什么界面???上述场景也可以通过具有所有抽象方法的抽象类来实现。
阿比吉特·欧哈

52

我使用接口的原因之一是因为它增加了代码的灵活性。假设我们有一个方法,该方法将一个类类型为Account的对象作为参数,例如:

public void DoSomething(Account account) {
  // Do awesome stuff here.
}

这样做的问题是方法参数固定为实现帐户。如果您永远不需要任何其他类型的帐户,那就很好。以本示例为例,该示例使用帐户界面作为参数。

public void DoSomething(IAccount account) {
  // Do awesome stuff here.
}

此解决方案并非针对某个实现而固定,这意味着我可以向其传递一个SuperSavingsAccount或ExclusiveAccount(均实现IAccount接口),并且为每个实现的账户获得不同的行为。


45

接口是实施者必须遵循的契约。抽象类允许合同以及共享的实现-接口无法做到的。类可以实现和继承多个接口。类只能扩展一个抽象类。

为什么要界面

  • 您没有默认或共享的代码实现
  • 您要共享数据合同(Web服务,SOA)
  • 每个接口实现者都有不同的实现(IDbCommand具有SqlCommandOracleCommand以特定方式实现接口
  • 您要支持多重继承

为什么要抽象


2
@Silver我阅读了您在博客中键入的大部分内容,但实际上我试图理解。我已经完成了WCF服务,公开的接口(但这只是一个独立的应用程序,没有上游或下游)。因此,尽管我已经很好地设计和实现了接口,但我还是无法正确理解它。我的问题是,实际上,您只是共享方法名称合同的方式对吗?这有何用处:(我知道它只是强制实现所有方法,但还有其他方法吗?在上面界面上的帖子中,第二点表示分享,这意味着您可以给出一个实际的实时示例
学习者

1
对于有关接口和SOA的实际示例,我们在.NET程序集中共享WCF接口DataContracts)(例如Contracts.Shared.dll),以便.NET客户端使用者可以轻松地进行互操作ChannelFactory避免通过“添加服务引用”生成代码等)。)或使用具有共享类型的添加服务引用
SliverNinja-MSFT 2012年

如果我只在抽象环境中声明抽象方法,那么抽象类将充当接口,那么为什么需要接口?
阿比吉特·欧哈

25

在此处输入图片说明

因此,在此示例中,PowerSocket对其他对象一无所知。这些对象都依赖于PowerSocket提供的Power,因此它们实现了IPowerPlug,这样它们就可以连接到它。

接口非常有用,因为它们提供了对象可以用来一起工作的契约,而无需彼此了解任何其他信息。


这是有道理的,但是我仍然很难理解,是否可以不为PowerSocket创建基类,而其他所有其他东西也可以在需要时继承它。从技术上讲,电源插座对其他类别一无所知。
altaaf.hussein

我认为是因为C#中不允许多重继承
Hugh Seagraves

22

一句话-因为多态

如果“对接口编程,而不是对接口编程”,则可以将共享相同接口(类型)的不同对象作为参数注入到方法中。这样,您的方法代码就不会与另一个类的任何实现耦合,这意味着它始终可以与同一接口的新创建对象一起使用。(开/关原则)

  • 研究依赖注入,并明确阅读“ 设计模式-GOF可重用的面向对象软件元素”

4

C#没有鸭子类型-仅仅因为您知道在一组具体类中实现了某种方法并不意味着您可以在调用该方法方面一视同仁。实施接口后,就该接口定义的内容而言,您可以将实现该接口的所有类都视为同一类事物。


3
您可以在.net4中使用动态类型来进行鸭嘴式。
托尼·霍普金森

4

我相信,在问这个问题时已经流了很多血,许多人试图通过解释普通人无法理解的类似机器人的术语来解决这个问题。

所以首先。要了解为什么要使用界面以及为什么要抽象,您需要了解它们的用途。我在申请工厂课程时亲自学习了这两点。您在此链接上找到了不错的选择

现在让我们基于我已经给出的链接进行深入研究。

您有可以根据用户要求更改的Vehicle类(例如添加TruckTankAirplane等)。

public class clsBike:IChoice
{
   #region IChoice Members
    public string Buy()
    {
       return ("You choose Bike");
    }
    #endregion
}

public class clsCar:IChoice
{
   #region IChoice Members
    public string Buy()
    {
       return ("You choose Car");
    }
    #endregion
}

两者都具有合同约定,只是说“我的班级”应采用“购买”方法

public interface IChoice
{
    string Buy();
}

现在,您看到,该接口仅强制执行该方法,Buy()但让继承的类决定在实现该方法时该怎么做。这是接口的局限性,使用纯接口,您可能最终会重复一些我们可以使用abstact自动执行的任务。在我们的示例中,假设购买每辆车都有折扣。

public abstract class Choice
{
    public abstract string Discount { get; }
    public abstract string Type { get; }
    public string Buy()
    {
       return "You buy" + Type + " with " + Discount;
}
public class clsBike: Choice
{
    public abstract string Discount { get { return "10% Discount Off"; } }
    public abstract string Type { get { return "Bike"; } }
}

public class clsCar:Choice
{
    public abstract string Discount { get { return " $15K Less"; } }
    public abstract string Type { get { return "Car"; } }
}

现在,使用Factory类,您可以实现相同的目的,但是使用抽象,则让基类执行该Buy()方法。

简介:接口协定让继承类来执行实现,而抽象类协定可以初始化实现(可以被Inherit类覆盖)


2

使用界面,您可以执行以下操作:

1)创建隔离的接口,这些接口可为您的实现提供不同的切入点,从而使接口更具凝聚力。

2)允许在接口之间使用相同名称的多个方法,因为嘿,您没有冲突的实现,只是一个签名。

3)您可以独立于实现而对界面进行版本控制和取消配置,以确保遵守合同。

4)您的代码可以依靠抽象而不是依赖,允许进行聪明的依赖注入,包括注入测试Mocks等。

我敢肯定还有更多原因,这些只是其中的几个。

抽象类允许您使用部分具体的基础进行工作,这与接口不同,但是具有其自身的特质,例如使用模板方法模式创建部分实现的能力。


您忽略了最重要的事情:实现一个接口使您的类可以被需要实现该接口的任何代码使用,而相关代码不必了解您的类。
2014年

2

作为@ user2211290的真实示例,答案:

两者的ArrayList有接口IList。下面我们有一个a string[]和a,List<string>并使用IList通过一种方法检查它们两者:

string[] col1 = { "zero", "one", "two", "three", "four"};
List<string> col2 = new List<string>{ "zero", "one", "two", "three"};

//a methode that manipulates both of our collections with IList
static void CheckForDigit(IList collection, string digit)
{
    Console.Write(collection.Contains(digit));
    Console.Write("----");
    Console.WriteLine(collection.ToString()); //writes the type of collection
}

static void Main()
{
    CheckForDigit(col1, "one");   //True----System.String[]
    CheckForDigit(col2, "one");   //True----System.Collections.Generic.List`1[System.String]



//Another test:

    CheckForDigit(col1, "four");   //True----System.String[]
    CheckForDigit(col2, "four");   //false----System.Collections.Generic.List`1[System.String]
}

1

您只能从一个抽象类继承。您可以从多个接口继承。这决定了我在大多数情况下使用的是什么。

抽象类的优点是您可以有一个基本实现。但是,在IDisposable的情况下,默认实现是没有用的,因为基类不知道如何正确清理。因此,接口将更适合。


1

抽象类和接口都是契约。

合同的概念是您指定一些行为。如果您说已经实施,则表示您已同意合同。

选择抽象而不是接口。

抽象类的任何非抽象后代都将实现合同。

任何实现该接口的类都将实现合同。

因此,当您要指定所有后代必须执行的某些行为时,可以使用abstract,并保存自己定义一个单独的接口,但是现在满足此有效聚合合同的所有内容都必须是后代。


1

接口是对现实(对象)的抽象(类)的抽象(原型)。

接口应指定合同条款,而不提供类提供的实现。

接口是规格:

  • 界面是设计时的工件,用于指定概念的固定行为,因为它是单独的和静态的。

  • 类是实现时间工件,用于在现实交互和移动时指定现实的移动结构。

什么是接口?

当您观察到猫时,您可以说它是具有四个爪子,头,躯干,尾巴和头发的动物。您可以看到他可以走路,跑步,吃饭和喵喵叫。等等。

您刚刚使用其属性和操作定义了一个接口。这样,您没有定义任何形式的操作,而仅定义了特征和功能,却不知道事物如何工作:您已经定义了能力和区别。

因此,尽管在UML中我们称其为类图中的一个类,但实际上它还不是一个类,因为我们可以定义私有和受保护的成员,以开始对工件进行深入了解。这里不要混淆,因为在UML中,接口与C#中的接口略有不同:它就像是对抽象原子的部分访问点。因此,我们说一个类可以实现多个接口。因此,这是同一回事,但并非如此,因为C#中的接口都用于抽象化抽象并将抽象化限制为访问点。这是两种不同的用途。因此,UML中的类表示与编程类的完整耦合接口,而UML接口则表示编程类的某个部分的解耦接口。确实,UML中的类图不执行该实现,并且其所有工件都在编程接口级别。当我们将UML类映射到编程类时,它是抽象抽象到具体抽象的转换。有一个微妙之处可以解释设计领域和编程领域之间的二分法。因此,从编程接口的角度来看,UML中的类是编程类,同时考虑内部隐藏的事物。

当尴尬的方式不可用时,接口还可以模拟多重继承。例如,cat类将实现从动物接口派生的cat接口。这个cat类还将实现以下接口:走路,跑步,吃饭和发出声音。这弥补了在类级别上没有多重继承的不足,但是每次您需要重新实现所有功能时,便无法像现实本身那样充其量地考虑现实。

为了理解这一点,我们可以参考Pascal Object编码,在其中您可以在一个单元中定义接口和实现部分。在接口中定义类型,并在实现中实现类型:

unit UnitName;

interface

type
  TheClass = class
  public
    procedure TheMethod;
  end;

implementation

class procedure TheClass.TheMethod;
begin
end;

在这里,接口部分与UML类设计匹配,而接口类型则是另外一回事。

因此,在我们的业务中,我们只有一个词,界面来提名两个截然不同但相似的事物,这是造成混乱的根源。

同样在C#中,编程接口允许补偿开放类型上不存在真正的通用多态性,而不会真正实现目标,因为您失去了强类型的稳定性。

毕竟,接口是必需的,以允许不兼容的系统进行通信,而不必担心内存对象的实现和管理(如(分布式)通用对象模型所引入的那样)。

什么是课程?

从外部角度定义现实的还原之后,您可以从内部角度进行描述:这是您定义数据处理和消息管理的类,以使您封装的现实得以实现并进行交互。使用实例的对象。

因此,在UML中,您实现了分形浸入机器的轮子中,并描述了状态,相互作用等,从而能够实现要处理的现实片段的抽象。

这样,从编译器的角度来看,抽象类在某种程度上等效于接口。

更多信息

协议(面向对象编程)

C#-接口

C#-类


1

让我告诉您有关飞行烤面包机的信息。

飞行烤面包机

当然,在很多情况下,您都可以构建运行的软件系统而无需声明或实现任何接口:除类之外,任何面向对象的软件设计都可以实现。

再者,任何软件系统也可以用汇编语言或更好的机器代码实现。之所以使用抽象机制,是因为它们倾向于使事情变得更容易。接口就是这样一种抽象机制。

因此,恰好发生了某些非平凡的面向对象设计,如果您使用接口,那么它们将更容易实现,在这些情况下,实际上需要使用接口。

这些非平凡的设计与多重继承有关,多重继承以其“真实”形式出现,即当一个类不仅从一个基类继承而是从两个或多个基类继承时。这种真正的形式在C#中是不可能的,但是在诸如C#和Java之类的语言出现之前,统治语言是C ++,它完全支持真正的多重继承。不幸的是,事实证明真正的多重继承并不是一个好主意,因为它极大地增加了语言的设计复杂度,并且还引起了各种问题,例如著名的“钻石问题”。(请参阅J Francis的“多重继承的确切问题是什么?”

因此,如果有人想建立一个“ flying toaster”类,那么他们将从一些现有的“ toaster”类以及一些现有的“ flying”类中继承。他们可能会遇到的问题是,烤面包机类的电源可能是壁装插座,而飞行器类的电源可能是鸽子粮,而由此产生的新类将是两者都有,或者不清楚是哪一个。(钻石问题。)

C#和Java之类的语言的创建者决定不允许真正的多重继承,以保持该语言的简单性并避免诸如Diamond问题之类的陷阱。但是,仍然需要某种形式的多重继承(或至少非常非常需要),因此在这些语言中,他们引入了接口,以支持较少形式的多重继承,同时避免了真正多重继承的问题和复杂性。

在这种较小的多重继承形式中,不允许您拥有一个从多个基类继承的类,但至少可以从一个或多个接口继承。因此,如果您想构建一个飞行烤面包机,则不能同时继承一些现有的烤面包机类和某个飞行类,但是您可以做的是继承现有的烤面包机类,然后公开一个您自己实现的飞行接口,可能使用您已经从烤面包机继承的任何方式。

因此,除非您感到需要创建一个集合两个不同且不相关的功能集的类,否则您将不需要任何形式的多重继承,因此也就不需要声明或实现任何接口。


0

接口允许类设计器使最终用户非常清楚可用的方法。它们也是多态性的组成部分。


在您的第一个陈述中说得好。但是我不明白您的第二个发言,请您能举例说明一下吗?
学习者2012年

0

我不会发布针对抽象类的接口的定义,因为我认为您非常了解该理论,并且假设您了解SOLID原理,因此让我们开始实践。

如您所知,接口不能包含任何代码,因此其缺点很容易理解。

如果您需要初始化提供构造函数的类的属性,或者要提供实现的一部分,则抽象类将非常适合接口所不允许的操作。

因此,在一般情况下,当您需要向客户端提供将继承/扩展您的类的构造函数或任何代码时,应首选抽象类而不是接口


-2

抽象类适用于相关实体,其中as接口可用于不相关实体。

例如,如果我有两个实体,例如动物和人类,那么我将进入接口,好像我必须详细说老虎,狮子并想与动物联系,然后选择动物抽象类。

如下所示

   Interface             
   ____|____
  |        |
Animal   Human



  Animal (Abstract class)
   __|___
  |      |
Tiger   Lion
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.