Why does embedding multiple interfaces with the same property show an "ambiguity" warning?

Related post: C # interface method ambiguity

Code from the same source:

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

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

    private interface IAllYourBase : IBase1, IBase2
    {
    }

    private class AllYourBase : IAllYourBase
    {
        private int _percentage;

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

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

      

(But does not answer my question - "WHY do contracts become ambiguous?")

Given:

An interface is a contract over which an implementation class must execute.

If two (or more) interfaces request the same contract, and the interface passes them forward, then the class implements both of them and ACCEPTS that shared contracts should only serve as one contract for the implementing classes (without providing an explicit implementation). Then,

  • Why does the compiler show an "ambiguity" warning on shared contracts?

  • Why does the compiler fail to compile when trying to access an ambiguous contract through the interface (iayb.Percentage)?

I would like to know which benefit compiler works with this constraint?

Edit: providing a real world where I would like to use contracts between interfaces as one contract.

public interface IIndexPriceTable{
      int TradeId{get;}
      int IndexId{get;}
      double Price{get;}
}

public interface ILegPositionTable{
      int TradeId {get;}
      int LegId {get;}
      int Position {get;}
}

public interface ITradeTable {
      int TradeId{get;}
      int IndexId{get;}
      int LegId{get;}
      //others
}

public interface IJoinedTableRecord : IIndexPriceTable, ILegPositionTable, ITradeTable {
     //Just to put all contracts under one interface and use it as one concrete record, having all information across different tables.
}

      

  • Why would I like to have 3-TradeId, 2-LegId, 2-IndexId in my merged table record?
+3


source to share


6 answers


Since the interface IAllYourBase

doesn't declare a property Percentage

.

When you assign an instance to a AllYourBase

variable IAllYourBase

, the compiler should output the call IBase1.Percentage

to either IBase2.Percentage

:

callvirt   instance int32 IBase1::get_Percentage()

      

or



callvirt   instance int32 IBase2::get_Percentage()

      

They are different members on different types, and just because they have the same signature does not mean that they are interchangeable.

In your real-world situation, you may need more subtle interfaces that define common properties.

+3


source


The solution is to re-define the Percentage property with a new keyword like this:

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

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

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

private class AllYourBase : IAllYourBase
{
    private int _percentage;

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

private void Foo()
{
    IAllYourBase iayb = new AllYourBase();
    int percentage = iayb.Percentage; //OK
} 

      



Note:

The C # approach to interfaces is very different from the Bjarne StrouStrup plan approach in C ++ 14 . In C #, you have to assert that the interface is implementing a class by modifying the class itself, while in C ++ 14 it only needs methods that match the definition of the interface. So the code in C # has more dependencies that are coded in C ++ 14.

+9


source


Because the compiler cannot figure out which implementation of the underlying interface ( IBase1.Percentage

or IBase2.Percentage

) you are trying to get, because your interface IAllYourBase

accepts after both of them and both of them have their own Percentage

.

Put it this way: just because two interfaces have a property with the same name and type, it doesn't mean that the property should work the same in both interfaces. Even if a common interface inherits from two interfaces with the same members, the compiler cannot simply combine two seemingly identical properties into one, since they are members of two different contracts.

+3


source


I see your thought. I guess the main advantage of this compiler limitation is that it's better to have it and then not. That is, there is more harm, then your unintended interface will be ignored, then it will come in handy (if any) from this weird case that you want this behavior.

By the way, is there any real world scenario out there that would benefit the desired behavior?

0


source


If an interface inherits two other interfaces that will have similar names, then one of two conditions must apply:

  1. Both interfaces inherit the same element from the other interface. The other interface should be publicly available, but it can be documented that it exists solely for inheritance, and that consumers are not expected to declare variables or type parameters.
  2. An interface that inherits other interfaces declares its own member of the same name as `new` . This is a good approach when one interface declares a read-only property and another declares a write-only property with the same name; an interface that combines the two can declare a read-write property that is implemented using a read-only property and a write-only setter property. I'm not sure if this would be good in many other situations.

    If someone doesn't do one of these things, it's probably best that the compiler doesn't try to guess. Imagine it has interfaces IListOfDigits

    , the method Add

    adds an integer 0-9 to the list and IBigNumber

    , the method Add

    adds the number arithmetically. One also has an IListOfDigitsRepresentingBigNumber interface that both inherits. Given the IListOfDigitsRepresentingBigNumber

    called myThing

    while holding the digits "5,4,3,2", what should the result of myThing.Add (1) be? Should the change myThing

    hold "5,4,3,2,1" (effect IListOfDigits.Add

    ) or "5,4,3,3" (effect IBigNumber.Add

    )? If you do one of the above, the compiler has no trouble figuring out which method to Add

    use. Otherwise, if both methods can accept int

    , it won't have a prompt.

    By the way, generics and overloading are an interesting case. If a IFoo<T,U>

    has members void Bar(T param)

    and void Bar(U param)

    , you cannot declare the class as an implementation IFoo<int,int>

    . Alternatively, you can declare a class Foo<T,U>

    as an implementation IFoo<T,U>

    and then declare some other class as inheriting from Foo<int,int>

    , because even if T

    they U

    are of the same type, the compiler will still resolve the overloads using T

    and U

    .

0


source


The string int percentage = iayb.Percentage;

has no idea that it is dealing with a class AllYourBase

, as soon as it is, it implements the interface IAllYourBase

.

So, suppose I tried to execute the same statement using a class DoubleBase

:

private class DoubleBase : IAllYourBase
{
    int IBase1.Percentage { get; set; } = 10;

    int IBase2.Percentage { get; set; } = 20;
}

      

What does it matter int percentage

?

0


source







All Articles