Saturday, 3 August 2013

Multiple Inheritance in C#

From the many features being added to Java 8, there's one that has really caught my eye, Default Interface Implementation aka Defender Methods (the other ones are really necessary stuff, but nothing out of the ordinary, as it's stuff that should have been in the language many years ago).

The main motivation for these Default methods is the same as for Extension Methods in C#, allowing you to add methods to an interface without breaking existing code. Let's think in C# and the pre-Linq times. You had an IEnumerable interface, and felt the need to add to it methods like "Contains, All, Any, Skin, Take...". Well, if you just add those methods, all your existing classes implementing IEnumerable would need to be updated to add an implementation of those methods there... well, quite a hard job. The solution to this in C# were Extension Methods. In Java they were about to mimic this same approach (and indeed you'll still find old references to "Java Extension Methods") but in the end they opted for a much more powerful one, Default Interfaces.

public interface SimpleInterface {
public void doSomeWork();
//A default method in the interface created using 'default' keyword
default public void doSomeOtherWork(){
System.out.println('DoSomeOtherWork implementation in the interface');

You've always been able to implement multiple interfaces, now they're adding behaviour to interfaces, so this winds up in you being able to inherit behaviour from multiple "places"!!!

Extension Methods in C# also adds behaviour to interfaces, and as such you also get a sort of Multiple Inheritance, but in a quite more "second class" limited way. Extension Methods are just a compiler artifact and the method resolution is done at compile time, so you lose the runtime magic of polymorphism-overriding-vTables. When you extend an existing Interface with new methods, if then a derived class implements one of those extra methods, polymorphism won't work to invoke that overriden method. Let's see an example:


public interface IPerson
{
 string Name{get;set;}
 string SayHello();
}

public static class IPersonExtensions
{
 public static string SayBye(this IPerson person)
 {
  return person.Name + " says Bye from Extension Method";
 }
}

public class Person:IPerson
{
 public string Name {get;set;}
 public Person(string name)
 {
  this.Name = name;
 }
 
 public string SayHello()
 {
  return this.Name + " says Hello";
 }
 public string SayBye()
 {
  return this.Name + " says Bye";
 }
}



public class Program
{
 public static void Main()
 {
  //the extension method is good to add a SayBye to the IPerson interface
  //but as a compile time artifact, it will not take into account if the implementing class has "overriden" it
  IPerson p1 = new Person("Iyan");
  Console.WriteLine(p1.SayBye()); //writes "says Bye from Extension Method"
  
  Person p2 = p1 as Person;
  Console.WriteLine(p2.SayBye()); //writes "says Bye"
 }
}

In the example above, the IPerson interface has been extended with an additional method SayBye through the IPersonExtensions static class. Then the Person class tries to override SayBye with its own implementation, but polymorphism won't work when it's invoked in a Person object via IPerson, and the IPerson implementation in the Extension Method will be used rather than the one in Person.

Other limitation of Extension Methods is that they are not visible via Reflection, I mean, if you call Type.GetMethodInfos() it won't return in that list those methods that could be accessed in that type via Extension Methods. As a consequence of this, they don't play well with dynamic either when you expect that dynamic resolution to be done through Reflection. You'll find more information on this here and here

With all this in mind, I decided to simulate this "Multiple Inheritance of Behaviour" in C#. The idea is simple and effective, though not much wrist friendly. For each interface whom you'd like to add behavior you create a class that implements the interface and contains those "default methods", and then, for your normal classes implementing that interface, you'll add a reference to that class for the default implementation, and for those methods for which you don't want to override the default implementation, you just delegate calls to that reference.

public interface IValidable
{
 bool AmIValid();
}

public interface IPersistable
{
 string Persist();
 
 int EstimateTimeForFullPersist();
}

public class DefaultValidable: IValidable
{
 //just one single method, no calls to other methods in the class, so no need for an Implementer field
 public bool AmIValid()
 {
  return this.GetType().GetProperties(BindingFlags.Public|BindingFlags.Instance).All(PropertyInfo => PropertyInfo.GetValue(this) != null);
 }
}

public class DefaultPersistable: IPersistable
{
 public IPersistable Implementer { get; set; }
 public DefaultPersistable()
 {
  this.Implementer = this;
 }
 
 public string Persist()
 {
  //notice how we have to use [this.Implementer.Estimate] here to allow method overriding to work,
  //cause using [this.Estimate] would invoke the Default (NotImplementedException) one.
  if (this.Implementer.EstimateTimeForFullPersist() > 1500)
   return this.ToString();
  else
  {
   //complex logic here
   return "this is the result of a complex logic";
  }
 }
 
 public int EstimateTimeForFullPersist()
 {
  throw new NotImplementedException();
 }
}

public class Book: IValidable, IPersistable
{
 protected IValidable ValidableImplementation { get; set; }
 protected IPersistable PersistableImplementation { get; set; }
 
 public Book(DefaultValidable validableImp, DefaultPersistable persistableImp)
 {
  this.ValidableImplementation = validableImp;
  this.PersistableImplementation = persistableImp;
 }
 
 public bool AmIValid()
 {
  //delegate to default implementation
  return this.ValidableImplementation.AmIValid();
 }

 public string Persist()
 {
  //delegate to default implementation
  return this.PersistableImplementation.Persist();
 }
 
 public int EstimateTimeForFullPersist()
 {
  //do not delegate to default implementation, "override" it
  return 50;
 }
}

public class Program
{
 public static void Main()
 {
  DefaultPersistable defPersistable = new DefaultPersistable();
  Book b = new Book(new DefaultValidable(), defPersistable);
  defPersistable.Implementer = b;
  
  Console.WriteLine("Is the Book valid: " + b.AmIValid().ToString());
  Console.WriteLine("Book.Persist: " + b.Persist());
 }
}

Looking at the implementation above you'll notice that the code is more straightforward for DefaultValidable than for DefaultPersistable. No defaul method in DefaultValidable invokes other methods in the interface, while in DefaultPersistable the Persist method invokes EstimateTimeForFullPersist, which means that in order to invoke the correct implementation if EstimateTimeForFullPersis has been overriden, we have to use the Implementer reference for those invokations.

You should also notice that while the above technique allows "Multiple Inheritance of Behavior" it does not address the real motivation of Default Methods in Java, extending the contract of an existing interface with new methods without breaking existing code. You still need to resort to Extension Methods in C# for that.

All this has reminded me of an interesting post I read months ago about using ES6 proxies as a way to implement multiple inheritance in JavaScript. The idea is pretty interesting, but I see an important flaw, the instanceof operator won't work with the "base classes". Applying instanceof to the proxy object will tell you that it's neither an instance of base1 or base2. This could be fixed if instanceof were also interceptable in the proxy, but seems like (at least in the current proposal) it's not.

By the way, as it's somehow related to this article, I'll reference here my write up about Extension Methods and Mixins/Traits from last year.

No comments:

Post a Comment