35

考虑以下示例:

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

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

4

9 回答 9

23

明确实施:

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

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

于 2011-08-16T15:37:30.587 回答
12

假设您希望两个属性都访问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; }
   }
}

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

于 2011-08-16T15:49:30.883 回答
10

显式地实现接口。

显式接口成员实现是引用完全限定接口成员名称的方法、属性、事件或索引器声明

有关详细教程,请参阅此 MSDN 页面

interface AllYourBase : IBase1, IBase2
{
   int IBase1.Percentage { get; set; }
   int IBase2.Percentage { get; set; }
}
于 2011-08-16T15:37:18.947 回答
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;
于 2011-08-16T15:37:51.457 回答
7

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

接口的进一步隔离(接口隔离原则)怎么样?

  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!!!
        }
    }
于 2012-03-09T08:35:30.727 回答
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
}

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

于 2011-08-16T15:44:02.933 回答
3

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

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

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
{
}
于 2011-08-16T15:57:26.453 回答
3

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

像这样的东西:

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

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

于 2017-06-16T11:35:51.037 回答
1
void Foo()
{
   IAllYourBase base = new AllYourBase();
   int percentage = base.Percentage; // Fails to compile. Ambiguity between 'Percentage' property.
}

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

于 2011-08-16T15:42:55.933 回答