121

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

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

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

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

像往常一样,MSDN 看起来很吓人,那里没有一条清晰的线(伙计们,请原谅那些进入高级开发的人,我强烈认为任何代码或文章都应该到达任何看到它的人的脑海,因此就像许多其他人所说的那样,MSDN没用)。

面试官说:

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

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

4

16 回答 16

94

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

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. 他们将在界面中找到答案。

于 2012-06-06T13:30:58.200 回答
54

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

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

这样做的问题在于,方法参数是针对帐户的实现而固定的。如果您永远不需要任何其他类型的帐户,这很好。以这个例子为例,它使用帐户接口作为参数。

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

此解决方案不固定于实现,这意味着我可以将 SuperSavingsAccount 或 ExclusiveAccount (都实现 IAccount 接口)传递给它,并为每个实现的帐户获取不同的行为。

于 2016-03-08T12:45:07.333 回答
49

接口是实现者必须遵守的契约。抽象类允许契约和共享实现——这是接口所不能拥有的。类可以实现和继承多个接口。类只能扩展一个抽象类。

为什么接口

  • 您没有默认或共享代码实现
  • 您想要共享数据合同(Web 服务、SOA)
  • 您对每个接口实现者都有不同的实现(IDbCommandSqlCommand并且OracleCommand以特定方式实现接口
  • 你想支持多重继承

为什么要抽象

于 2012-06-06T13:14:24.423 回答
25

在此处输入图像描述

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

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

于 2017-08-25T12:18:08.320 回答
23

一言以蔽之——因为多态

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

  • 研究Dependency Injection 并明确阅读GOF的 Design Patterns - Elements of Reusable Object-Oriented Software
于 2013-11-27T23:09:08.487 回答
6

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

所以首先。要了解为什么接口和为什么抽象,您需要了解它们的用途。我在申请工厂类时亲自学习了这两个。你在这个链接上找到了一个很好的教程

现在让我们根据我已经提供的链接进行挖掘。

您的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
}

并且两者都有合同 Ichoice,简单地说 My Class 应该有 Buy 方法

public interface IChoice
{
    string Buy();
}

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

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"; } }
}

现在使用工厂类,你可以实现同样的事情,但在使用抽象时,你让基类执行Buy()方法。

总结:接口契约让继承类做实现,而抽象类契约可以初始化实现(可以被继承类覆盖)

于 2015-11-11T12:51:34.983 回答
4

C# 没有鸭子类型——仅仅因为您知道某个方法是在一组具体类中实现的,并不意味着您可以在调用该方法时对它们一视同仁。实现接口允许您将实现它的所有类视为相同类型的事物,关于该接口定义的内容。

于 2012-06-06T13:12:17.907 回答
3

这是一个简单的例子:

两者都Array实现List了接口IList。下面我们有 astring[]和 aList<string>并使用IList只用一种方法操作它们:

string[] myArray = { "zero", "one", "two", "three", "four"};
List<string> myList = 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));  //checks if the collection has a specific digit
    Console.Write("----");
    Console.WriteLine(collection.ToString()); //writes the type of collection
}

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



//Another test:

    CheckForDigit(myArray, "four");   //True----System.String[]
    CheckForDigit(myList, "four");   //false----System.Collections.Generic.List`1[System.String]
}
于 2019-01-17T13:34:43.980 回答
2

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

1) 创建分离的接口,提供不同的实现方案,从而实现更具凝聚力的接口。

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

3)您可以独立于您的实现对接口进行版本化和配置,以确保满足合同。

4)你的代码可以依赖抽象而不是具体化,允许智能依赖注入,包括注入测试模拟等。

我敢肯定还有很多原因,这些只是其中的一小部分。

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

于 2012-06-06T13:15:20.457 回答
2

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

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

接口是规格:

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

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

什么是接口?

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

您刚刚定义了一个接口及其属性和操作。因此,您没有定义任何作案手法,而只是定义了特性和能力,却不知道事情是如何运作的:您已经定义了能力和区别。

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

接口还允许在不可用时以尴尬的方式模拟多重继承。例如,猫类将实现从动物接口派生的猫接口。这个猫类也将实现这些接口:走、跑、吃和发出声音。这弥补了类级别缺少多重继承的情况,但是每次您需要重新实现所有内容时,您最多不能像现实本身那样考虑现实。

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

unit UnitName;

interface

type
  TheClass = class
  public
    procedure TheMethod;
  end;

implementation

class procedure TheClass.TheMethod;
begin
end;

在这里,接口部分与 UML 类设计相匹配,而接口类型因此是其他东西。

因此,在我们的业务中,我们使用一个词interface来指定两个不同但相似的事物,这会造成混淆。

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

毕竟,接口对于允许不兼容的系统进行通信是必要的,而不必担心内存中对象的实现和管理,就像(分布式)公共对象模型引入的那样。

什么是班级?

从外部角度定义现实的简化后,您可以从内部角度对其进行描述:这是您定义数据处理和消息管理的类,以允许您封装的现实栩栩如生并进行交互谢谢使用实例的对象。

因此,在 UML 中,您实现了对机械轮子的分形沉浸,并描述了状态、交互等,以便能够实现您想要处理的现实片段的抽象。

因此,从编译器的角度来看,抽象类在某种程度上等同于接口。

更多信息

协议(面向对象编程)

C# - 接口

C# - 类

于 2019-09-30T19:16:46.850 回答
1

抽象类和接口都是契约。

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

抽象而不是接口的选择是。

抽象类的任何非抽象后代都将实现契约。

相对

任何实现接口的类都将实现契约。

因此,当您想指定所有后代必须实现的某些行为时,您可以使用抽象,而不必自己定义一个单独的接口,但是现在满足这个有效聚合契约的所有东西都必须是后代。

于 2012-06-06T13:28:03.067 回答
1

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

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

于 2012-06-06T13:14:20.863 回答
1

让我告诉你关于飞行烤面包机的事。

飞行烤面包机

当然,在许多情况下,您可以构建一个工作软件系统,而无需声明或实现任何接口:任何面向对象的软件设计都可以只使用类来实现。

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

所以,碰巧有一些非常重要的面向对象设计,如果你使用接口,这些设计会更容易实现,在这些情况下,接口实际上是必要的。

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

因此,如果有人想构建一个“飞行烤面包机”类,他们将继承一些现有的“烤面包机”类以及一些现有的“飞行”类。他们可能遇到的问题是,烤面包机类的电源很可能是墙上的插座,而飞行器类的电源很可能是鸽子粮,而由此产生的新类要么不知何故两者都有,或者不清楚它会拥有哪一个。(钻石问题。)

C# 和 Java 等语言的创建者决定不允许真正的多重继承,以保持语言简单并避免像钻石问题这样的陷阱。然而,某种形式的多重继承仍然是必要的,(或者至少非常需要),因此在这些语言中,它们引入了接口作为支持较少形式的多重继承的一种手段,同时避免了真正多重继承的问题和复杂性。

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

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

于 2019-11-02T12:52:25.597 回答
0

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

于 2012-06-06T13:10:40.667 回答
0

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

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

如果您需要初始化提供构造函数的类的属性,或者您想提供部分实现,那么抽象类将非常适合不允许您这样做的接口。

因此,一般来说,当您需要向客户端提供构造函数或任何代码来继承/扩展您的类时,您应该更喜欢抽象类而不是接口

于 2012-06-06T13:14:13.077 回答
-2

抽象类是为相关实体创建的,而接口可用于不相关的实体。

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

如下所示

   Interface             
   ____|____
  |        |
Animal   Human



  Animal (Abstract class)
   __|___
  |      |
Tiger   Lion
于 2013-11-27T14:06:16.347 回答