【问题标题】:How do interfaces solve the diamond problem?接口如何解决钻石问题?
【发布时间】:2012-08-27 09:55:18
【问题描述】:

我需要和你讨论一件事。我一直在阅读有关接口的信息,它是类与接口之间的契约,该类将提供接口所有方法的实现。为了解决著名的钻石问题,我们有接口。 喜欢(我正在使用 C#)

public interface IAInterface
{
     void aMethod();
}

public interface IBInterface
{
     void aMethod();
}

现在

public class aClass : IAInterface, IBInterface
{
     public void aMethod()
     {

     }
}    

通过上面的代码,据说aClass已经提供了接口的两个方法的实现。我的困惑是:如果它是类和接口之间的契约,那么我们怎么能说与两个接口的契约都被履行了?

一个真实世界的例子, 就像我和杰克有合同,我会给他 5 美元,同时我也和本有合同,我会给他 5 美元,现在通过向其中一个(杰克或本)提供 5 美元我怎么说我和他们两个都履行了合同?

我的问题可能看起来很幼稚,但这让我很困惑。

【问题讨论】:

    标签: c# oop diamond-problem


    【解决方案1】:

    接口保证一组给定的方法将被实现。接口本身并不关心代码将如何实现。与扩展类相反,当您扩展一个类时,您使用的是一些预定义的代码。

    如果你用相同的方法扩展两个类,比如methodA,并调用这个方法,那么编译器将不知道它需要运行哪一个。另一方面,当您使用相同的方法实现两个接口时,由于实现是在您自己的代码中编写的,编译器会知道它需要去哪里找到实现。

    因此,基本上,当您实施某些东西时,您就可以保证您将提供一些服务。

    编辑:我会尝试以您的示例并以某种面向对象的方式将其转换,这有望使事情变得更清晰。

    Jack 和 Ben 会保证他们会借给你钱,而另一方面,你保证会偿还,所以,从 OO 的角度来看,你可以这样看:

    public interface IPay
    {
        public void Pay(Individual i);
    }
    
    public class MySelf : IPay
    {
        public void Pay(Individual i)
        {
            ...
        }
    }
    
    public interface ILend
    {
        public void Lend(Individual i);
    }
    
    public class Jack : ILend //Same applies for Ben
    {
        public void Lend(Individual i)
        {
            ...
        }
    }
    

    这就是我将如何处理您指定的问题。

    至于另一个垃圾示例,您可以这样做:

    public interface ITakeTrash
    {
        public void TakeTrash(Trash t);
    }
    
    public Son : ITakeTrash
    {
        public void TakeTrash(Trash t);
    }
    
    public class Mum  //Same applies for dad
    {
        Son mySon = ...
    
        mySon.TakeTrash(t);
    }
    

    你的父母不知道如何处理垃圾,但是,他们知道知道如何处理它,这就是他们关心的(就像上面一样,你只关心 Ben 和 Jack 能不能给你钱,而其他方只关心你能不能还钱,所涉及的人真的都不在乎你是怎么做的。

    我确实了解您的目标,基本上是说如果我使用具有相同名称的方法实现两个不同的接口来执行不同的操作。如果您最终陷入这种情况,我认为那是由于设计不当造成的。我不建议建立一个做无数不同事情的类,这是原因之一。

    另一方面,您可能会问,如果我有 2 个接口用同名的方法做类似的事情怎么办?然后,我认为答案是,由于接口 类似的事情,并且你有同名的方法,很可能你正在做同样的事情,因此,你不需要把自己弄糊涂了。

    我希望这次编辑有助于阐明这个问题。

    【讨论】:

    • 我确切地知道编译器会如何看待它。但是面向对象语言的灵感来自于现实世界。如果是合同,则必须与合同中涉及的每个人一起履行。
    【解决方案2】:

    实施好的方法不像给钱。就像倒垃圾一样。如果你答应爸爸妈妈倒垃圾,倒垃圾一次就够了。

    【讨论】:

    • 如果爸爸妈妈有不同的垃圾袋,我答应爸爸我会拿出他的垃圾,妈妈我会拿出她的垃圾,我只拿出爸爸的垃圾。我怎么能说我已经兑现了我对爸爸和妈妈的承诺?
    • 爸爸妈妈没有垃圾桶。他们只有垃圾的概念。你作为班级,实现垃圾桶的概念。这正是重点。没有方法,只有实现方法的要求。只要你这样做,两个界面都很开心。
    【解决方案3】:

    例如,我们有两个接口,它们都有相同的函数 PrintIt,而 MyClass 类实现了这两个接口。

    interface IAInterface
    {
      void PrintIt(string s);
    }
    
    interface IBInterface
    {
      void PrintIt(string s);
    }
    
    class MyClass : IAInterface, IBInterface
    {
      public void PrintIt(string s)
      {
        Console.WriteLine(s);
      }  
    }
    

    可以通过多个接口引用

    1. 类对象
    2. IAInterface接口参考
    3. IBInterface 接口参考

        MyClass _class = new MyClass();
        _class.PrintIt("Class"); 
    
        IAInterface _aInterface = (IAInterface)_class;
        _aInterface.PrintIt("IAInterface");
    
        IBInterface _bInterface = (IBInterface )_class;
        _bInterface.PrintIt("IBInterface");
    

    您还可以提供显式接口实现...

    class MyClass : IAInterface, IBInterface
    {
      public void IAInterface.PrintIt(string s)
      {
        Console.WriteLine("AInterface - {0}", s);
      } 
    
      public void IBInterface.PrintIt(string s)
      {
        Console.WriteLine("BInterface - {0}", s);
      }   
    }
    

    【讨论】:

    • 在上面的显式接口实现编译时会产生错误。 修饰符“public”对此项无效
    【解决方案4】:

    由于两个方法具有相同的名称和相同的签名(意味着相同的返回值类型和相同的参数定义),因此假设两个方法是相同的。

    【讨论】:

      【解决方案5】:

      在多重继承中,使用时会出现菱形问题,

      public class A 
      {
          public virtual  void aMethod(); 
      }
       public class B
      {      
       public virtual void aMethod();
      }
      

      现在

      public class aClass : A, B
       {      
      public override void aMethod()  
          {       } 
      }  
      

      在上面的代码中,对于 vTable 中的同一个 aMethod(),将为 aClass 创建两个 vPtr。因此出现歧义(菱形概率)

      但是当你使用接口的时候,就没有 vTable 的概念了。因为 vTable 在基类和派生类场景之间在调用它们之间的差异实现时很有用。在这种情况下,接口不包含任何实现,因此没有 vPtr、vTable,因此没有菱形问题。

      【讨论】:

        【解决方案6】:

        如果您必须从不同的接口实现相同命名的方法。试试这个。

        interface IFoo
        {
            void SayHello();
        }
        interface IBar : IFoo
        {
            new void SayHello();
        }
        class myFooBarImp : IFoo, IBar
        {
             void IFoo.SayHello()
            {
                Console.WriteLine("Hello from IFoo implementation");
            }
        
             void IBar.SayHello()
            {
                Console.WriteLine("Hello from IBar implementation");
            }
        
            public void SayHello()
            {
                Console.WriteLine("Hello from SayHello");
            }
        
        }
        
        
        
         class Program1
        {      
            public static void Main()
            {
        
                myFooBarImp obj = new myFooBarImp();
                obj.SayHello();
                (obj as IFoo).SayHello();
                (obj as IBar).SayHello();
                return;        
        
             }
        }
        

        【讨论】:

          【解决方案7】:

          父类被设计为由子类使用,而接口被设计为由子类实现。因此,即使两个父接口都有相同的方法,但继承它不会给子类造成混乱。

          参考:https://dotnettutorials.net/lesson/multiple-inheritance-csharp/

          【讨论】:

            猜你喜欢
            • 2022-01-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-04-19
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多