Friday 14 April 2017

Overriding Extension Methods

The other day I found that there is a proposal to add "multiple inheritance of behaviour" to C#, by means of allowing you to add "implemented methods" to interfaces. This is basically what was done in Java 8 with "default methods". I talked about them some years ago.

So far we could get a limited taste of this by means of Extension Methods (as you know this has been the way to extend IEnumerable with all the Linq methods). So, what's the difference with default methods?

Extension Methods are a bit more like a hack. They are like a limited form of the object expansion (monkey patching) that you do in JavaScript (when you assign a new method to a [[prototype]] or the object itself. I say limited cause there is a main problem with it. It's a static, compile-time artifact, hence it can not be overridden. I mean, let's say I have this code:


interface IPerson
{
 String Name {get;set;}
}



static class PersonExtensions
{
 public static string SaySomething(this IPerson person)
 {
  return "Hello, I am " + person.Name;
 }
}



So I have an IPerson interface and we sort of expand it by adding the SaySomething extension method. The problem here is that if I have a French class were that method is redefined (it's not properly overriden, as it is not part of the interface itself, just an extension), if I use the interface, I will continue to call the extension method rather than the overriden one. The compiler just sees an interface and a method call not directly in the contract, but in the extension, and statically binds that call to the extension method. With default methods in Java we would get the dynamic behaviour.


public class French: IPerson
{
 public string Name {get; set;}

 public French(string name)
 {
  this.Name = name;
 }

 public string SaySomething()
 {
    return "Bonjour, je suis " + this.Name;
 
 }

}



IPerson iP = new French("Charlie");

Console.WriteLine(iP.SaySomething());
// prints "Hello, I am..."
//it calls the extension method (PersonExtensions.SaySomething) rather than French.SaySomething

Could we do something to "fix" this with extension methods? Well, I can think of a rather ugly hack, but it could come handy. We would have to include in those extension methods that we want to make overridable the code that checks if type for the specific instance that we receive in this call is redefining the method. Something like this (just a POC, I'm not checking parameters, only the method name):

 static class PersonExtensions
{
    
 public static string SaySomethingOverridable(this IPerson person)
 {
    
  if (person.GetType().GetMethod("SaySomethingOverridable") != null)
  {
   dynamic aux = person;
   return aux.SaySomething();
  }
  else
  {
   return "Hello, I am " + person.Name; 
  }
 }
}

IPerson iP = new French("Charlie");
            
//this one goes through the Extension method that contains the type checking and allows the overriding
//so it prints "Bonjour, je suis..."        
Console.WriteLine(iP.SaySomethingOverridable());

No comments:

Post a Comment