我真的不明白接口存在的原因。据我所知,这是c#中不存在的多继承的一种工作(至少我是这么被告知的)。

我所看到的是,您预定义了一些成员和函数,然后必须在类中再次重新定义它们。从而使接口成为冗余。它只是感觉像句法……嗯,垃圾对我来说(请没有冒犯的意思。Junk是指无用的东西)。

在下面的例子中,我将创建一个名为Pizza的基类,而不是一个接口。

简单示例(取自不同的堆栈溢出贡献)

public interface IPizza
{
    public void Order();
}

public class PepperoniPizza : IPizza
{
    public void Order()
    {
        //Order Pepperoni pizza
    }
}

public class HawaiiPizza : IPizza
{
    public void Order()
    {
        //Order HawaiiPizza
    }
}

当前回答

这些都是很好的例子。

另外,在switch语句的情况下,您不再需要在每次希望里约热内卢以特定方式执行任务时进行维护和切换。

在你的披萨例子中,如果你想做一个披萨,界面就是你所需要的,从那里每个披萨都有自己的逻辑。

这有助于减少耦合和圈复杂度。您仍然需要实现逻辑,但在更广泛的情况下,您需要跟踪的内容将更少。

对于每个披萨,您可以跟踪特定于该披萨的信息。其他披萨有什么并不重要,因为只有其他披萨需要知道。

其他回答

在Python中没有鸭子类型的情况下,c#依赖接口来提供抽象。如果类的依赖项都是具体类型,则不能传入任何其他使用类型的接口,可以传入实现该接口的任何类型。

Pizza示例很糟糕,因为您应该使用一个处理排序的抽象类,而pizzas应该重写Pizza类型。

当您有一个共享属性,但是您的类从不同的地方继承,或者当您没有任何可以使用的公共代码时,您可以使用接口。例如,这是用过的东西,可以被处置为IDisposable,你知道它会被处置,你只是不知道当它被处置时会发生什么。

接口只是一个契约,它告诉你一个对象可以做一些事情,什么样的参数和期望什么样的返回类型。

接口也可以通过菊花链来创建另一个接口。这种实现多个接口的能力使开发人员可以在不改变当前类功能的情况下向类中添加功能(SOLID原则)

O = "类应该对扩展开放,对修改关闭"

如果我正在使用一个API来绘制形状,我可能想使用DirectX或图形调用,或OpenGL。因此,我将创建一个接口,它将从您调用的内容中抽象出我的实现。

所以你调用一个工厂方法:MyInterface i = MyGraphics.getInstance()。然后,你有一个契约,所以你知道你可以在MyInterface中期望什么功能。你可以调用i。drawrectangle或i。drawcube并且知道如果你把一个库换成另一个库,函数是被支持的。

如果您正在使用依赖注入,这就变得更加重要,因为您可以在XML文件中交换实现。

所以,你可能有一个加密库可以导出,供一般使用,而另一个加密库只出售给美国公司,区别在于你改变了配置文件,而程序的其余部分不会改变。

这在。net中的集合中被大量使用,就像你应该只使用,例如,列表变量,不要担心它是一个数组列表还是LinkedList。

只要您编写了接口代码,那么开发人员就可以更改实际的实现,而程序的其余部分则保持不变。

这在单元测试时也很有用,因为您可以模拟出整个接口,因此,我不需要访问数据库,而是只返回静态数据的模拟出来的实现,因此我可以测试我的方法,而不用担心数据库是否需要维护。

对我来说,刚开始的时候,只有当你不再把它们看作是让你的代码更容易/更快编写的东西时,它们的意义才变得清晰——这不是它们的目的。它们有很多用途:

(这里就没有披萨的比喻了,因为这个比喻的用法不太容易想象)

假设你正在屏幕上制作一款简单的游戏,游戏中会有与你互动的生物。

答:通过在前端和后端实现之间引入松散耦合,它们可以使您的代码在将来更容易维护。

你可以这样写,因为这里只会有喷子:

// This is our back-end implementation of a troll
class Troll
{
    void Walk(int distance)
    {
        //Implementation here
    }
}

前端:

function SpawnCreature()
{
    Troll aTroll = new Troll();
    
    aTroll.Walk(1);
}

两周后,市场营销决定你也需要半兽人,因为他们在twitter上看到了他们,所以你必须做如下事情:

class Orc
{
    void Walk(int distance)
    {
        //Implementation (orcs are faster than trolls)
    }
}

前端:

void SpawnCreature(creatureType)
{
    switch(creatureType)
    {
         case Orc:

           Orc anOrc = new Orc();
           anORc.Walk();

          case Troll:

            Troll aTroll = new Troll();
             aTroll.Walk();
    }
}

你可以看到这是如何变得混乱的。你可以在这里使用一个接口,这样你的前端就会被编写一次(这里是重要的部分)测试,然后你可以根据需要插入更多的后端项目:

interface ICreature
{
    void Walk(int distance)
}

public class Troll : ICreature
public class Orc : ICreature 

//etc

前端则为:

void SpawnCreature(creatureType)
{
    ICreature creature;

    switch(creatureType)
    {
         case Orc:

           creature = new Orc();

          case Troll:

            creature = new Troll();
    }

    creature.Walk();
}

前端现在只关心接口ICreature -它不关心喷子或兽人的内部实现,而只关心他们实现ICreature的事实。

从这个角度来看,需要注意的一点是,您也可以很容易地使用抽象生物类,从这个角度来看,这具有相同的效果。

你可以将创建的内容提取到工厂:

public class CreatureFactory {

 public ICreature GetCreature(creatureType)
 {
    ICreature creature;

    switch(creatureType)
    {
         case Orc:

           creature = new Orc();

          case Troll:

            creature = new Troll();
    }

    return creature;
  }
}

我们的前端会变成:

CreatureFactory _factory;

void SpawnCreature(creatureType)
{
    ICreature creature = _factory.GetCreature(creatureType);

    creature.Walk();
}

现在,前端甚至不需要有实现Troll和Orc的库的引用(假设工厂在一个单独的库中)——它不需要知道任何关于它们的信息。

B:假设你拥有在你的同质数据结构中只有某些生物才有的功能,例如:

interface ICanTurnToStone
{
   void TurnToStone();
}

public class Troll: ICreature, ICanTurnToStone

前端可以是:

void SpawnCreatureInSunlight(creatureType)
{
    ICreature creature = _factory.GetCreature(creatureType);

    creature.Walk();

    if (creature is ICanTurnToStone)
    {
       (ICanTurnToStone)creature.TurnToStone();
    }
}

C:依赖注入用法

大多数依赖注入框架在前端代码和后端实现之间存在非常松散的耦合时才能工作。如果我们以上面的工厂为例,让我们的工厂实现一个接口:

public interface ICreatureFactory {
     ICreature GetCreature(string creatureType);
}

我们的前端可以通过构造函数注入(例如MVC API控制器)(通常):

public class CreatureController : Controller {

   private readonly ICreatureFactory _factory;

   public CreatureController(ICreatureFactory factory) {
     _factory = factory;
   }

   public HttpResponseMessage TurnToStone(string creatureType) {

       ICreature creature = _factory.GetCreature(creatureType);
   
       creature.TurnToStone();

       return Request.CreateResponse(HttpStatusCode.OK);
   }
}

使用我们的DI框架(例如Ninject或Autofac),我们可以设置它们,以便在运行时在构造函数中需要ICreatureFactory时创建一个CreatureFactory实例——这使我们的代码美观而简单。

这也意味着当我们为控制器编写单元测试时,我们可以提供一个模拟的ICreatureFactory(例如,如果具体实现需要访问DB,我们不希望我们的单元测试依赖于它),并轻松地测试控制器中的代码。

D:还有其他用途,例如,你有两个项目A和B,由于“遗留”原因,它们没有很好地组织起来,而A有B的参考。

然后在B中发现需要调用a中已经存在的方法的功能。由于您获得的是循环引用,因此不能使用具体实现来实现。

你可以在B中声明一个接口,然后由A中的类实现。你在B中的方法可以被传递一个实现接口的类的实例,即使具体对象是a中的类型。