Saturday, January 30, 2010

Extension Methods

Extension methods are static methods that can be invoked using instance method syntax. In effect, extension methods make it possible to extend existing types and constructed types with additional methods.
Note   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.
Extension members of other kinds, such as properties, events, and operators, are being considered but are currently not supported.

Declaring Extension Methods

Extension methods are declared by specifying the keyword this as a modifier on the first parameter of the methods. Extension methods can only be declared in static classes. The following is an example of a static class that declares two extension methods:
namespace Acme.Utilities
{
   public static class Extensions
   {
      public static int ToInt32(this string s) {
         return Int32.Parse(s);
      }
      public static T[] Slice(this T[] source, int index, int count) {
         if (index < 0 || count < 0 || source.Length – index < count)
            throw new ArgumentException();
         T[] result = new T[count];
         Array.Copy(source, index, result, 0, count);
         return result;
      }
   }
}
Extension methods have all the capabilities of regular static methods. In addition, once imported, extension methods can be invoked using instance method syntax.

Importing Extension Methods

Extension methods are imported through using-namespace-directives (§9.3.2). In addition to importing the types contained in a namespace, a using-namespace-directive imports all extension methods in all static classes in the namespace. In effect, imported extension methods appear as additional methods on the types that are given by their first parameter and have lower precedence than regular instance methods. For example, when the Acme.Utilities namespace from the example above is imported with the using-namespace-directive
using Acme.Utilities;
it becomes possible to invoke the extension methods in the static class Extensions using instance method syntax:
string s = "1234";
int i = s.ToInt32();               // Same as Extensions.ToInt32(s)
int[] digits = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int[] a = digits.Slice(4, 3);      // Same as Extensions.Slice(digits, 4, 3)

Extension Method Invocations

The detailed rules for extension method invocation are described in the following. In a method invocation (§7.5.5.1) of one of the forms
expr . identifier ( )
expr . identifier ( args )
expr . identifier < typeargs > ( )
expr . identifier < typeargs > ( args )
if the normal processing of the invocation finds no applicable instance methods (specifically, if the set of candidate methods for the invocation is empty), an attempt is made to process the construct as an extension method invocation. The method invocation is first rewritten to one of the following, respectively:
identifier ( expr )
identifier ( expr , args )
identifier < typeargs > ( expr )
identifier < typeargs > ( expr , args )
The rewritten form is then processed as a static method invocation, except for the way in which identifier is resolved: Starting with the closest enclosing namespace declaration, continuing with each enclosing namespace declaration, and ending with the containing compilation unit, successive attempts are made to process the rewritten method invocation with a method group consisting of all accessible extension methods with the name given by identifier imported by the namespace declaration's using-namespace-directives. The first method group that yields a non-empty set of candidate methods is the one chosen for the rewritten method invocation. If all attempts yield empty sets of candidate methods, a compile-time error occurs.
The preceeding rules mean that instance methods take precedence over extension methods, and extension methods imported in inner namespace declarations take precedence over extension methods imported in outer namespace declarations. For example:
using N1;
namespace N1
{
   public static class E
   {
      public static void F(this object obj, int i) { }
      public static void F(this object obj, string s) { }
   }
}
class A { }
class B
{
   public void F(int i) { }
}
class C
{
   public void F(object obj) { }
}
class X
{
   static void Test(A a, B b, C c) {
      a.F(1);            // E.F(object, int)
      a.F("hello");      // E.F(object, string)
      b.F(1);            // B.F(int)
      b.F("hello");      // E.F(object, string)
      c.F(1);            // C.F(object)
      c.F("hello");      // C.F(object)
   }
}
In the example, B's method takes precedence over the first extension method, and C's method takes precedence over both extension methods.

No comments:

Post a Comment