Friday, April 8, 2011

Interface ambiguity and implicit implementation

A while ago I had a small problem with interface ambiguity. The subject can be quite tricky to understand, so I decided to write a small blog post about the problem. This problem occurs when to interfaces implement the same property and a third interface tries to implement that property. Any references to that property through the third interface will result in a wonderful Ambiguity between 'xxx.yyy' and 'zzz.yyy'.

So let's jump in and consider the following interfaces:

public interface IShippable
{
 string Size { get; set; }
 string LabelText { get; set; }
}

public interface IAnimal
{
 string Size { get; set; }
 string Name { get; set; }
}

public interface IZooAnimal : IAnimal, IShippable
{
 string ZooName { get; set; }
}

Now let’s implement the interface by the following class:

public class Giraffe : IZooAnimal
{
 public string ZooName { get; set; }
 public string Size { get; set; }
 public string Name { get; set; }
 public string LabelText { get; set; }
}

The number of interface properties to implement by the Giraffe class is 5, but due to the implicit implementation, we only need to implement 4 properties. The compiler is able to figure out that the Size property is the implementation of both the IShippable and IAnimal interface.

Now let’s look at the problem. It might look like all is well, but consider the following program:

class Program
{
 static void Main(string[] args)
 {
  Giraffe giraffe = new Giraffe();
  giraffe.Name = "Iwana";
  giraffe.ZooName = "Artis, Amsterdam, The Netherlands";
  giraffe.Size = "2m";
  giraffe.LabelText = "Tool cargo";

  PrintSize(giraffe);
 }

 public static void PrintSize(IZooAnimal animal)
 {
  Console.WriteLine("Animal {0} is {1}", animal.Name, animal.Size);
 }
}

It looks great, but this piece of code is not able to compile. The animal.Size will give the following error: Ambiguity between 'Tester.IAnimal.Size' and 'Tester.IShippable.Size'.

Why!?
At first this error looked really weird. I tried to see if the class and the other interfaces had the same problem, but the following compiles wonderful and prints the right information:

 //just the giraffe
 Console.WriteLine("Size is: {0}", giraffe.Size);

 //the IAnimal interface
 IAnimal animal = giraffe;
 Console.WriteLine("Size is: {0}", animal.Size);

 //the IShippable interface
 IShippable shippable = giraffe;
 Console.WriteLine("Size is: {0}", shippable.Size);

Yet, the following will not compile:

 //the IZooAnimal interaface
 IZooAnimal zooAnimal = giraffe;
 Console.WriteLine("Size is: {0}", zooAnimal.Size);

Implicit vs. explicit implementation
Interfaces are used to enforce contracts. The main advantage is that new interfaces can be implemented by existing classes without having to change the underlying type. An interface either can be implemented implicitly or explicitly. An explicit implantation will look like this:

public class Test : IDisposable
{
 void IDisposable.Dispose()
 {
 }
}

The main advantage of an explicit implementation is to prevent ambiguity. Let’s say that the IShippable.Size and the IAnimal.Size are two completely different things. To distinguish between the two, we would an explicit implementation that looks like this:

public class Giraffe : IZooAnimal
{
 public string ZooName { get; set; }
 public string Name { get; set; }
 public string LabelText { get; set; }
 string IShippable.Size { get; set; }
 string IAnimal.Size { get; set; }
}

The disadvantage of explicit implementation is that the Giraffe “looses” its Size property. If I want to read the size I have to explicitly cast the object to the correct interface. Another solution is to create properties ShippableSize and AnimalSize on the class and let the two interface sizes point to these properties.

The problem
But why did my code not compile? The problem is basically the assumption of implicit implementation. Keep in mind that IZooAnimal is not implementing anything, it is only inheriting from two interfaces. Because IZooAnimal is not responsible for the implementation, it doesn’t make any assumption about its base interfaces. Only Giraffe can make assumptions, as it is doing the actual implementation.

From the IZooAnimal perspective, there is no reason to assume that IAnimal.Size is the same as IShippable.Size, because there is no implantation involved when interfaces inherit from each other.

Solution
So how can we "fix" this problem? Well… if the Size property of both interfaces are the same, it should be declared in a new common interface. Let’s introduce a new interface called ISize:

public interface ISize
{
 string Size { get; set; }
}

public interface IShippable : ISize
{
 string LabelText { get; set; }
}

public interface IAnimal : ISize
{
 string Name { get; set; }
}

Now our code is able to compile correctly and do the thing we want.

Conclusion
The are a few conclusions that can be drawn from this:

  • Implicit implementation is only done by classes;
  • If interface properties or methods are the same, implent them by a common interface.

Discussion
If you like to discuss it more, please comment or visit this question on StackOverflow.

1 comment:

Please feel free to leave a comment. When you are using the option to react anonymous, please add your name to the comment ;-).