Extension methods and method overload resolution

Posted by Chris on October 04, 2006

Eric Gunnerson recently “posted an interesting piece of C# code”:http://blogs.msdn.com/ericgu/archive/2005/12/12/502890.aspx and asked what it does. As he explains in the “follow-up discussion post”:http://blogs.msdn.com/ericgu/archive/2005/12/13/503225.aspx, the reason why it does what it does is that we would not want the behaviour of our program to change due to a new method being added to a class we are subclassing. This is of course precisely the way it should be to avoid nasty problems. However, an interesting thought that struck me was that the extension methods feature in C# 3.0 and the method overload resolution rules they use can create precisely that problem.

Consider the following C# 3.0 code:

// Foo.cs
namespace thirdpartylib {
  public class Foo {
    private int x;
    private int y;
    public Foo(int x, int y) {
      this.x = x;
      this.y = y;
    }

    public int DoFoo() {
      return this.x + this.y;
    }

    public int X { get { return this.x; } }
    public int Y { get { return this.y; } }
  }
}

// ExtendFoo.cs
using thirdpartylib;
namespace extensions {
  public static class ExtendFoo {
    public static int DoBar(this Foo theFoo, int z) {
      return theFoo.X - theFoo.Y - z;
    }
  }
}

// Program.cs
using System;
using thirdpartylib;
using extensions;
namespace LINQConsoleApplication1 {
  class Program {
    static void Main(string[] args) {
      Foo foo = new Foo(1, 2);

      int i = foo.DoFoo();
      int j = foo.DoBar(3);

      Console.WriteLine("DoFoo: {0}", i);
      Console.WriteLine("DoBar: {0}", j);
    }
  }
}

When this little program is executed it prints “DoFoo: 3″ and “DoBar: -4″ to the console. For those that have not seen C# 3.0, the interesting part is the static class ExtendFoo with it’s static method DoBar. The keyword @this@ before the first parameter is what is new in 3.0, and it is what lets us extend the Foo class to seemingly have a DoBar method that the program can call. By simply bringing them into scope (with the @using extensions;@ line) the compiler adds any static methods on static classes with the first parameter ‘decorated’ with the @this@ keyword to the applicable classes. Note though that this is only syntactic sugar, what is really happening (and which can easily be seen by reviewing the IL generated) is the same as if we would have written this:

int j = ExtendFoo.DoBar(foo, 3);

So, coming back to method overload resolution. For extension methods the rule is that the extension method is used if it does not clash with an instance method on the extended type. So, now consider what happens when this code is added to Foo:

    public int DoBar(short z) {
return this.x * this.y * z;
}

Now the behaviour of our program has changed into printing "DoBar: 6" instead of "DoBar: -4". Considering the IL that is generated with extension methods this is not at all strange, but I anticipate one or two developers might get caught by this. Microsoft does include a warning about extension methods in the PDC preview bits (??"Extension methods are less discoverable and more limited in functionality than instance methods. For those reasons, it is recommended that extension methods be used sparingly and only in situations where instance methods are not feasible or possible."??), but this in particular feels kind of creepy.

Trackbacks

Trackbacks are closed.

blog comments powered by Disqus