Kees C. Bakker
Kees C. Bakker

Reputation: 33421

Why can't .Net / C# understand interface inheritance with properties of the same name?

Consider the following class and interfaces:

    public interface A { string Property { get; set; } }

    public interface B { string Property { get; set; } }

    public interface C : A, B { }

    public class MyClass : C
    {
        public string Property { get; set; }
    }

Looks simple, right? Now consider the following program:

    static void Main(string[] args)
    {
        MyClass myClass = new MyClass();
        myClass.Property = "Test";

        A aTest = myClass;
        B bTest = myClass;
        C cTest = myClass;

        aTest.Property = "aTest";
        System.Console.WriteLine(aTest.Property);
        bTest.Property = "bTest";
        System.Console.WriteLine(bTest.Property);
        cTest.Property = "cTest";
        System.Console.WriteLine(cTest.Property);
        System.Console.ReadKey();
    }

Looks okay, but it will not compile. It gives me an Ambiguity exception:

Screenshot compiler

Why isn't C# able to figure this out? Is what I'm doing crazy from an architectural point of view? I'm trying to understand the why (I know it can be solved with casting).

EDIT

The problems arose when I introduced interface C. When I use MyClass : A, B I've got no problems at all.

FINAL

Just finised a blog about the subject: Interface Ambiguity and Implicit Implementation.

Upvotes: 7

Views: 1987

Answers (8)

Konstantin Oznobihin
Konstantin Oznobihin

Reputation: 5327

In short because it's ambiguous indeed.

Now more detailed story. As you've already seen there is explicit interface implementation, so you can have two different implementations for A.Property and B.Property and when you have only C there is no way you can tell if implementations are the same or not. Since C# "philosophy" is not to guess what you meant, but make you state it more clear when necessary, compiler does not choose either A.Property or B.Property, but reports an error.

Upvotes: 8

SWeko
SWeko

Reputation: 30902

There are a lot of answers, and all of them are right, as explicit interface implementation is the answer to your problem.

I'll try to clarify the motivation behind this design with a somewhat convoluted example:

Let's say I have an interface for people that run (with possible implementations like LongDistanceRunner, Jogger, MarathonMan, etc)

public interface IRunner 
{
   void Run();
}

and an interface for devices that can be turned on and ran (with possible implementations BathTub, Application, Dishwasher, etc)

public interface IRunnable
{
   void Run();
}

Now I want to create and interface for a IMusicallJogger (implementations like JoggerWithIpod,BoomBoxJogger, etc)

public interface IMusicalJogger : IRunner, IRunnable {}

public class BoomBoxJogger : IMusicalJogger
{
   // code here
}

BoomBoxJogger bbJogger = new BoomBoxJogger();

Now, when I say bbJogger.Run() what should my object do? Should it start running across the park, or should it turn on the boombox, or both, or something else entirely? If I implement both the class and the callsite, it might be obvious that I want my joggers to do both, but what if I control just the callsite? And what if there are other implementations of the interface that do something else? And what if my jogger starts running across the park, when it's used in a context where it is considered like a device (through casting).

That's where explicit interface implementation comes into play.

I have to define my class like this:

public class BoomBoxJogger : IMusicalJogger
{
   void IRunner.Run() //implementation of the runner aspect
   {
      Console.WriteLine("Running through the park");
   }

   void IRunnable.Run() //implementation of the runnable aspect
   {
      Console.WriteLine("Blasting out Megadeth on my boombox");
   }

   public void Run() //a new method defined in the class itself 
   {
      Console.WriteLine("Running while listening to music");
   }

}

and then, when I call, I have to specify what aspect of my jogger I want to use:

BoomBoxJogger bbJogger = new BoomBoxJogger();
((IRunner).bbJogger).Run(); // start running
((IRunnable).bbJogger).Run(); // blast the boombox
//and of course you can now do
bbJogger.Run //running while listening

((IMusicalJogger)jogger).Run(); //compiler error here, as there is no way to resolve this.

Hope I helped clarify the concept.

Upvotes: 0

Nix
Nix

Reputation: 58562

You need explicit interface implementation:

public interface A { string Property { get; set; } }

public interface B { string Property { get; set; } }

public interface C : A, B { }

public class MyClass : C
{
    string B.Property { get; set; }
    string A.Property { get; set; }
}

When it comes time to call them you are going to have to do:

MyClass c = new MyClass();
Console.WriteLine("Property A is ": ((A)c).Property);

Why don't you do:

public class MyClass : C
{
    string B.Property { get; set; }
    string A.Property { get; set; }
    string B { get { return B.Property; } set { B.Property=value; } }
    string A { get { return A.Property; } set { A.Property=value; } }

}

And it should be noted this is bad design, if you are going to expose an interface C, make sure you find a better way to expose A/B.Property.

Upvotes: 4

NightDweller
NightDweller

Reputation: 923

When you inherit from a single interface the compiler can determine exactly which method you are interested in implementing when you add the new method.

However when multiple interfaces have the same method, the underlying (and correct) assumption is that each interface expects a DIFFERENT implementation for the method, due to the fact that those methods or properties are defined on different interfaces.

So the compiler tells you that these different interfaces require an explicit implementation for each of these properties.

The fact that two interfaces share the same NAME for a property or method is arbitrary - there is no reason to assume that they share anything OTHER then the name, so the compiler protects you from making the mistake of implicitly treating them in the same way.

Upvotes: 3

Aliostad
Aliostad

Reputation: 81680

Because what you are doing is not right. A and B are clashing and have the same name for the property... you need to use Explicit implementation of interface.

Reference here.

Upvotes: 0

Ernest Friedman-Hill
Ernest Friedman-Hill

Reputation: 81724

What's to figure out? cTest is of type "C", and it inherits "Property" from two different classes; the compiler doesn't know which one you want. This sort of behavior is inherited from C++; it's the classic example of "why multiple inheritance is a Pandora's box."

Other object-oriented languages -- Java is a notable example -- avoid this problem by definition : like-named/like-signatured methods are fused in a common descendent.

Upvotes: 3

Mitch Wheat
Mitch Wheat

Reputation: 300719

you need to explicity implement both properties from each interface:

public class MyClass : C     
{         
    string A.Property { get; set; }    
    string B.Property { get; set; }      
} 

Upvotes: 2

Daniel Daranas
Daniel Daranas

Reputation: 22634

It is not simple, and it doesn't look simple either. In case of a name collision between two interfaces, .NET needs to ask you which interface are you trying to implement. Its way to ask you this is via the ambiguity error.

If you didn't have this kind of errors, you would end up implementing interfaces by chance.

Upvotes: 2

Related Questions