【问题标题】:C# interface method ambiguityC#接口方法歧义
【发布时间】:2011-10-28 04:39:59
【问题描述】:

考虑以下示例:

interface IBase1
{
   int Percentage { get; set; }
}

interface IBase2
{
   int Percentage { get; set; }
}

interface IAllYourBase : IBase1, IBase2
{
}

class AllYourBase : IAllYourBase
{
   int percentage;

   int Percentage {
      get { return percentage; }
      set { percentage = value; }
   }
}

void Foo()
{
   IAllYourBase iayb = new AllYourBase();
   int percentage = iayb.Percentage; // Fails to compile. Ambiguity between 'Percentage' property.
}

在上面的示例中,要调用的 Percentage 属性之间存在歧义。假设IBase1IBase2 接口可能不会被更改,我将如何以最干净、最首选的方式解决这种歧义?

更新

根据我使用显式接口实现得到的响应,我想提一下,虽然这确实解决了问题,但对我来说并没有以理想的方式解决它,因为我使用我的 AllYourBase 对象作为 @987654326 @ 大多数时候,从不作为IBase1IBase2。这主要是因为IAllYourBase 还具有由AllYourBase 实现的接口方法(我未能在上面的代码sn-p 中详细说明这些方法,因为我认为它们无关紧要)并且我也想访问这些方法。一直来回转换会变得非常乏味并导致代码混乱。

我确实尝试了一种解决方案,该解决方案涉及在 IAllYourBase 中定义 Percentage 属性而不使用显式接口实现,这似乎至少消除了编译器错误:

class IAllYourBase : IBase1, IBase2
{
   int Percentage { get; set; }
}

这是一个有效的解决方案吗?

【问题讨论】:

  • +1:恕我直言不应该存在的有趣问题。
  • base 即使是局部变量也不是一个好名字,因为它可能与用于从派生类中访问基类成员的base 关键字混淆。跨度>
  • @Jesse 实际上根本不合法。 base 始终是关键字。
  • 如果你能提供更多关于你的类结构在这里代表什么的详细信息,它们很有可能被重新设计,你就可以摆脱这个问题
  • @Jesse:我知道这是无效的,这是一个人为的例子。我什至没有编译它。但你明白了我想要表达的基本观点......

标签: c# oop interface ambiguity


【解决方案1】:

显式实现:

public class AllYourBase : IBase1, IBase2
{
    int IBase1.Percentage { get{ return 12; } }
    int IBase2.Percentage { get{ return 34; } }
}

如果你这样做,你当然可以像平常一样对待你的非模棱两可的属性。

IAllYourBase ab = new AllYourBase();
ab.SomeValue = 1234;

但是,如果您想访问 percent 属性,这将不起作用(假设确实如此,预期返回哪个值?)

int percent = ab.Percentage; // Will not work.

您需要指定要返回的百分比。这是通过转换到正确的接口来完成的:

int b1Percent = ((IBase1)ab).Percentage;

如你所说,可以重新定义界面中的属性:

interface IAllYourBase : IBase1, IBase2
{
    int B1Percentage{ get; }
    int B2Percentage{ get; }
}

class AllYourBase : IAllYourBase 
{
   public int B1Percentage{ get{ return 12; } }
   public int B2Percentage{ get{ return 34; } }
   IBase1.Percentage { get { return B1Percentage; } }
   IBase2.Percentage { get { return B2Percentage; } }
}

现在您已经通过不同的名称解决了歧义。

【讨论】:

  • 如果这样做,我将无法以IAllYourBase 的形式访问AllYourBase 对象中的Percentage 属性,对吗?这对我来说将是一个巨大的负面影响,因为我主要将此对象用作IAllYourBase,而不是IBase1IBase2。我发现有效的一件事是在IAllYourBase 接口中再次定义接口属性,这似乎也解决了歧义。
  • 反响很好。这不仅有帮助(尽管我赞成的其他回复也很有帮助)。
【解决方案2】:

假设您希望两个属性都访问 percent 成员变量,您可以使用显式接口实现并扩展 IAllYouBase 接口来完成此操作:

interface IAllYourBase : IBase1, IBase2
{
    new int Percentage { get; set; }
}

class AllYourBase : IAllYourBase
{
   int percentage;

   public int Percentage {
      get { return percentage; }
      set { percentage = value; }
    }

    int IBase1.Percentage {
      get { return percentage; }
      set { percentage = value; }
    }

    int IBase2.Percentage {
      get { return percentage; }
      set { percentage = value; }
   }
}

它并不漂亮,但它会给你我认为你所追求的行为。

【讨论】:

  • +1 - 这是约束最少的最干净的解决方案。
  • 如果两者都返回相同的值,则不需要显式实现。
  • 我什至不知道您可以在界面中使用 new 关键字。谢谢!
【解决方案3】:

显式实现接口。

显式接口成员实现是引用完全限定接口成员名称

的方法、属性、事件或索引器声明

详细教程请参见this MSDN page

interface AllYourBase : IBase1, IBase2
{
   int IBase1.Percentage { get; set; }
   int IBase2.Percentage { get; set; }
}

【讨论】:

  • 我收到“显式接口声明只能在类或结构中声明”错误。
【解决方案4】:

显式实现并访问它:

interface IAllYourBase : IBase1, IBase2 { } 
public class AllYourBase : IAllYourBase
{     
      int IBase1.Percentage { get{ return 12; } }     
      int IBase2.Percentage { get{ return 34; } } 
} 

IAllYourBase base = new AllYourBase();    
int percentageBase1 = (base as IBase1).Percentage;
int percentageBase2 = (base as IBase2).Percentage;

【讨论】:

    【解决方案5】:

    如果您实际上不需要为Percentage 属性返回不同的值,则可以通过分别从每个接口而不是“主”接口“派生”来消除编译器错误:

    public class AllYourBase : IBase1, IBase2
    {
        // No need to explicitly implement if the value can be the same
        public double Percentage { get { return 12d; } }
    }
    

    当然,如果您确实需要单独的值,那么您必须显式实现接口,通过适当类型的引用访问属性:

    public class AllYourBase : IBase1, IBase2
    {
        // No need to explicitly implement if the value can be the same
        public double IBase1.Percentage { get { return 12d; } }
        public double IBase2.Percentage { get { return 34d; } }
    
    }
    

    还有代码:

    public void SomeMethod()
    {
        AllYourBase ayb = new AllYourBase();
        IBase1 b1 = ayb
        double p1 = b1.Percentage;
    
        IBase2 b2 = ayb;
        double p2 = b2.Percentage;
    }
    

    显式实现接口时的一个重要考虑因素是AllYourBase 本身不再具有Percentage 属性。只有通过类型为接口之一的引用访问对象时才能访问它:

    public void SomeMethod()
    {
        AllYourBase ayb = new AllYourBase();
        double d = ayb.Percentage;   // This is not legal
    }
    

    更新:查看您的编辑,您的解决方案很好,假设您不需要 IBase1IBase2 的不同行为。您的解决方案隐藏了这些属性,因此只能通过将对象转换为这两个接口之一来访问它们。

    【讨论】:

      【解决方案6】:

      虽然你已经接受了答案。我认为显式实现需要过多的冗余工作(在您的情况下两次实现相同的属性)!

      接口的进一步隔离如何(接口隔离原则)?

        internal interface IPercentage
          {
              int Percentage { get; set; }
          }
      
          internal interface IBase1 : IPercentage
          {
          }
      
          internal interface IBase2 : IPercentage
          {
          }
      
          internal interface IAllYourBase : IBase1, IBase2
          {
          }
      
          internal class AllYourBase : IAllYourBase
          {
              private int percentage;
      
              public int Percentage
              {
                  get { return percentage; }
                  set { percentage = value; }
              }
      
              void Foo()
              {
                  IAllYourBase iayb = new AllYourBase();
                  int percentage = iayb.Percentage; // Compiles now!!!
              }
          }
      

      【讨论】:

        【解决方案7】:

        虽然这些家伙提到的显式实现显然是正确的,但为了理智起见,请考虑以下场景(或者只是为了将来阅读您的代码的人这样做):

        如果这些百分比根据界面的不同有不同的含义,为什么不给它们一些有意义的名字呢?

        interface IBase1 
        { 
            int PercentageBase1 { get; set; } 
        }
        
        interface IBase2
        {
            int PercentageBase2 { get; set; } 
        }
        

        另一方面,如果它们具有相同的含义,为什么不在一个接口中只有一个百分比,而只是从另一个接口派生一个?

        interface IBase1 
        { 
            int Percentage { get; set; } 
        }
        
        interface IBase2 : IBase1
        {
        }
        

        但是,如果由于某种原因无法执行最后一个操作,请考虑为它们创建一个包含该属性的基本接口:

        interface IBase0
        {
            int Percentage { get; set; } 
        }
        
        interface IBase1 : IBase0
        { 
        }
        
        interface IBase2 : IBase0
        {
        }
        

        【讨论】:

          【解决方案8】:

          您可以在IAllYourBase接口中定义属性。

          类似这样的:

          interface IAllYourBase : IBase1, IBase2
          {
             new int Percentage { get; set; }
          }
          

          这将解决属性之间的歧义问题。并且您可以保留接口的结构。

          【讨论】:

            【解决方案9】:
            void Foo()
            {
               IAllYourBase base = new AllYourBase();
               int percentage = base.Percentage; // Fails to compile. Ambiguity between 'Percentage' property.
            }
            

            在此示例中,您使用 base 关键字作为对象名称,这可能会导致问题!!!

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-07-12
              • 2012-05-08
              • 1970-01-01
              • 2020-11-29
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多