A C# Feature Request: Extension Classes

April 10, 2008 c-sharp code

As you may have noticed, I’m a big fan of extension methods in C# 3.0, but they aren’t without their limitations. The first one you notice when you use them is the lack of extension properties. Since I like properties more than I like extension methods, this really bummed me out. So for kicks, I’d like to propose a C# feature request: Extension classes.

What’s an extension class?

An extension class is a class that adds methods, properties, etc. to another class. Given some class Foo:

public class Foo
{
    public int Value { get { return 3; } }
}

My proposed syntax is:

public class FooExtensions this Foo
{
    public int DoubledValue
    {
        get { return Value * 2; }
    }

    public void Hi()
    {
        Console.WriteLine("hi.");
    }
}

The magic bit up there is the this Foo. That basically says, “the this reference inside this class is actually of this other type Foo”. As you can see in the implementation of DoubledValue, it’s accessing the Value property, which is defined in Foo.

So, this solves the syntax problem for how to define an extension property. It makes sense, too, because if you’ve used extension methods, you’ve noticed that you always have to define them inside a “special” extension class anyway. This just moves the “extension-ness” indicator from the method level up to the containing class level.

What it’s not

Just to be clear, this isn’t the same as inheriting, partial classes, or open classes. There is no way to have a reference of type FooExtensions, because that class is not directly instantiable. It’s just decorating another class. Likewise, FooExtensions does not have priveleged access to Foo. It can only use public members just like outside code. And, of course, FooExtensions can’t replace any methods in Foo. No open classes.

And then?

OK, so we’ve got properties in the mix, is there anything else we could add?

public class FooExtensions this Foo
{
    public static SomeMethod() { /* ... */ }
}

How about static methods and properties? You could say there’s no value in doing this since you have to access statics through the class name anyway, but I disagree.

A design guideline that’s been floating around in C++ land for a long time is “prefer non-member functions over member functions”. The idea is that coupling and priveleged access to a class are bad, so if you can implement something outside of the class, that’s better. However, if doing that changes the calling convention, you’ve affected all your call sites with what should be an implementation detail. I don’t want users to have to know that SomeMethod() was implemented outside of Foo. That way, if I later need to move it into Foo (or vice versa), they aren’t affected.

Where this is going

So, if you can move more and more out of Foo, what’s left in it? The answer: data and validation. Foo itself is the only class that can have fields. In my world, the way I’d like to design classes is like this: the core class Foo contains its fields and provides accessors and mutators to prevent it from being put in a bad state. Foo basically is state and state change.

Almost everything else, the “things you can do with a Foo” would be pulled out into extension classes, likely in different namespaces. If you want to format a Foo for display, UI.FooExtensions will have methods for that. Need to serialize it? Serialization.FooExtensions.

Aside from being crazy about decoupling, there’s some more reasoning behind this. It turns out that state is actually one of the trickiest parts of programming. Computer scientists have been wrestling with its implications since the dawn of programming, and have tried all sorts of ways to get around it, up to the point of trying to abolish it completely (sort of) in “pure” functional languages.

Now the lack of real-world use of languages like that kind of hints that state is inevitable. One of the most useful things about computers is that they remember things. However, isolating and minimizing where and how they remember things, I think, will encourage us to write software that’s easier to reason about and maintain.