C#接口方法歧义

时间:2011-08-16 15:35:42

标签: c# oop interface ambiguity

考虑以下示例:

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对象作为大多数情况下IAllYourBase,绝不是IBase1IBase2。这主要是因为IAllYourBase也有接口方法(我没有在上面的代码片段中详述那些因为我认为它们无关紧要),这些方法由AllYourBase实现,我也想访问它们。来回播放会变得非常繁琐,导致代码混乱。

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

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

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

9 个答案:

答案 0 :(得分:21)

明确实施:

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

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

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

但是,如果你想访问百分比支柱,这将不起作用(假设确实如此,预期返回哪个值?)

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

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

答案 1 :(得分:10)

显式实现接口。

  

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

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

有关详细教程,请参阅this MSDN page

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

答案 2 :(得分:10)

假设您希望两个属性都能访问百分比成员变量,您可以使用显式接口实现并扩展 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; }
   }
}

它不漂亮,但它会给你我认为你想要的行为。

答案 3 :(得分:7)

明确地实施和访问它:

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;

答案 4 :(得分:7)

如果您实际上不需要为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的不同行为,查看您的编辑,您的解决方案就可以了。您的解决方案隐藏了这些属性,因此只有将对象转换为这两个接口之一才能访问它们。

答案 5 :(得分:4)

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

如何进一步分离界面(Inteface Segregation Principle)?

  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!!!
        }
    }

答案 6 :(得分:2)

虽然这些家伙提到的明确实施显然是正确的,但为了理智,请考虑遵循场景(或者只是为了将来阅读您的代码的人):

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

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
{
}

答案 7 :(得分:2)

您可以在IAllYourBase界面中定义属性。

这样的事情:

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

这将解决属性之间含糊不清的问题。你可以保持接口的结构。

答案 8 :(得分:1)

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

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