다음을 통해 공유


Extension methods in C#

Overview 

Extension methods are a new feature for C# 3.0 and I had the opportunity to implement them in the Compiler. These methods can then be called with instance syntax on any object that is convertible(see convertability section for details) to the first param of the method.

Validation Extension methods are defined in C# as

static class Extensions

{

  public static IEnumerable<T> Where<T>(this IEnumerable<T> sequence, Predicate<T> predicate)

  {

     foreach (T item in sequence)

     {

        if (predicate(item))

        {

            yield return item;

        }

      }

   }

}

Several Interesting things to Note here

  1. The method is define in a top level static class ( the class is directly under the namespace)
  2. The method is static and decorates its first param with a new param modifer this, this param is called the "instance parameter" and its an error to use the "this" modifers on any other parameter.
  3. No other parameter modifers ( ref, out etc) are allowed with "this" (so values types can't be passed by reference to an extension methods, VB will allow ref).
  4. The intance parameter can't be a pointer type.
  5. The method is public (its accessable to anyone who can get to its parentclass).
  6. The Type parameter used must be defined on the method and not the parentclass.

5 & 6 are required because extension methods are bound as instance methods and the argument to instance parameter is the object on which the method is called. This means that the class in which the extension methods are defined is just a container to put the extension method in will never be involved in binding it as an extension method e.g.

static class Extensions

{

public

static float Average(this System.Array array)

{

float average = 0;

for (int i = 0; i < array.Length; i++)

{

average += (int)array.GetValue(i);

}

return average / array.Length;

}

}

is bound as

int[] array = {34, 56, 100, 45, 23, 12};

array.Average(); note Extensions has nothing to do with the call

      7.  The instance parameter cannot have the type of the Type parameter.

       As this would make the impossible to bind the method as an instance method e.g.

public static class Extension3

{

    public static class Extension4

    {

        public static void Foo<T>(this T inst) { }

    }

}

Convertablity

The following conversion are defined on instance parameter on Extension methods

  1. Identity coversion (type S is S)

  2. Impicit reference conversions

  3. Boxing Convertions

Finding Extension methods

So how is the compiler to know which extension method to bind? The compiler looks for extension methods in the innermost namespace when the call is made for extension methods and then in all the namespaces imported by the "using" clause. This process is followed moving outward until we reach the topmost namespace.

Since extension methods can be imported and bound in the current context by the "using" clause and object which is assignable(see convertability section for details) to the instance parameter, all sorts of interesting possibilities open up for extending the methods implemented by a type. This can simply be done by importing a library of extension methods and using these methods as if they were declared on a type that you don't own.

Delegates

Calling methods is all well and good, but do extension methods work when instantiating a delegates?

Of course they do, (here is a good use of curried delegates). If you are not sure what curried delegates ( yum yum indian food ) are and how to the compiler uses them. Don't be alarmed, i will be covering that in the next post of Extension method binding.

e.g. using the Where extension defined above

double[] array = {34,45,67.12,95,25,69};

Func<IEnumerable<double>, Func<bool, double>> fun = array.Where<double>;

fun(new Func<bool, double>(Pred));

public static bool Pred(double arg)

{

if (arg > 10)

return true;

return false;

}

This works just as long as the instance param is a reference type :) (No Curry for you, Value Type)

Extension methods can also be called on Delegate e.g.

public delegate T del1<T>(T val);

And Extension method Exec defiend as

static class Extension

 {

     public static T Exec<T>(this del1<T> source, T param)

     {

      return source(param);

     }

}

We can write client code like

del1<int> d1 = t.func<int>;

d1.Exec(100);

 

This ends the first post on extension methods, next how exactly extension methods are bound.