C# interface method ambiguity

六眼飞鱼酱① 提交于 2019-11-29 01:02:24

Implement explicitly:

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

If you do this, you can of course treat your non-ambigous properties just like normal.

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

However, if you want to access the percentage prop this will not work (Suppose it did, which value would be expected in return?)

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

you need to specify which percentage to return. And this is done by casting to the correct interface:

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

As you say, you can redefine the properties in the interface:

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

Now you have resolved the ambiguity by distinct names instead.

Assuming that you want both properties to access the percent member variable you can accomplish this using explicit interface implementation and extending the IAllYouBase interface:

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

It's not pretty, but it will give you the behavior I think you're after.

Implement the interfaces explicitly.

An explicit interface member implementation is a method, property, event, or indexer declaration that references a fully qualified interface member name

See this MSDN page for a detailed tutorial.

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

Explicitly implement and access it:

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;

If you don't actually need to return different values for the Percentage property, you can eliminate the compiler error by "deriving" from each interface separately, rather than the "master" interface:

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

Of course if you do need separate values, then you'll have to explicitly implement the interfaces, and access the property via an appropriately typed reference:

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

}

And the code:

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

    IBase2 b2 = ayb;
    double p2 = b2.Percentage;
}

One important consideration when implementing the interfaces explicitly is that AllYourBase itself no longer has a Percentage property. It can only be accessed when the object is accessed via a reference typed as one of the interfaces:

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

UPDATE: Looking at your edit, your solution is fine, assuming you don't need different behaviour for IBase1 and IBase2. Your solution hides those properties, so they will only be accessible by casting the object to one of those two interfaces.

Though you have already accepted the answer. I would argue that explicit implementation asks too much of redundant work (Implementing the same property twice, in your case) !

How about further segregation of interface (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!!!
        }
    }

While explicit implementation the guys have mentioned is obviously correct, please consider following scenarions just for the sake of sanity (or do it just for a person reading your code in the future):

If these percentages have different meanings depending on the interface, why not to give them some meaningful names?

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

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

And, on the other hand, if they have the same meaning, why not to have just one Percentage in one of the interfaces and just derive one from another?

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

interface IBase2 : IBase1
{
}

However, if the last operation is not possible for whatever the reason is, please consider creating a base interface containing that property for both of them:

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

interface IBase1 : IBase0
{ 
}

interface IBase2 : IBase0
{
}

You can define the property in the IAllYourBase interface.

Something like this :

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

This will resolve the problems with the ambiguity between the properties. And you can keep the structure of the interfaces.

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

In this example you use the base keyword for an object name and that may be causing the problem!!!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!