Thursday, July 12, 2012

What are extension methods in C#?

Here's a really basic example.

An extension method in C# is a static method defined in a static class with the this keyword placed in front of the first parameter.
public static MyStaticClass {
   public static void DescribeYourself(this int value)
       System.Console.WriteLine("I am an integer with value " + value);

public static void Main() {
   int i = 5;

I am an integer with value 5

Do you see what's going on here?  The keyword this causes that static method to effectively behave as though you added an instance method to the int class.


Now let's look at a real world example.

Database frameworks often generate code from a data model.  The code follows the pattern of a  container class with properties that expose sets of entitity classes which have properties exposing data.

The container is the gateway to the database and sets of entities to tables with their elements to rows and properties to columns.

For example:

public class Container {
    public Set<Entity> Entities { get; set; }

public class Entity {
   public int Id { get; set; }
   public string Name { get; set; }

A typical usage of the above would be:
var db = new Container();
var query = db.Entities.Where(c => c.Name == "Foo");
Console.WriteLine("The entity named Foo has Id = " + query.Single());

What if we want something that was a bit more graceful?

Something like:

var db = new Container();
var query = db.Entities.QueryByName("Foo");
Console.WriteLine("The entity named Foo has Id = " + query.Single());

The logical answer starts with, "We have to add a method to the class which defines the type of db.Entities."

Looking to the Container class we see the type of Entities is Set<Entity>.  Can we add a method to that type?  No we cannot (to this point I'm 99% sure but please correct me if I'm wrong here).

Here is where an extension method can give us what we want.

public static class EntityExtensions {
   public static IQueryable<Entity>(
                   this Set<Entity> entities, 
                   string name) {
          return entities.Where(c => c.Name == "Foo");

See how we got around that fact that we couldn't add a method to the type Set<Entity>?  The net effect as as if we did though.  It's quite cool.