The next proposal in our C# Futures series considers the possibility of offering extension properties. This is a much desired feature, but it is commonly believed to not be possible because it would introduce memory leaks.

In actuality, all of the plumbing necessary for offering extension properties since .NET 4.0. The secret is that you can use a ConditionalWeakTable class to store the mapping between objects and extension fields. In fact, ConditionalWeakTable was created specifically to offer this capability for dynamically typed languages.

Sam Harwell demonstrates how to correctly use this class:

private static readonly ConditionalWeakTable<T, StrongBox<FieldType>> _extensionField_f; public static FieldType get_f(T obj) { StrongBox<FieldType> box; if (!_extensionField_f.TryGetValue(obj, out box)) return default(FieldType); return box.Value; } public static void set_f(T obj, FieldType value) { StrongBox<FieldType> box = _extensionField_f.GetOrCreateValue(obj); box.Value = value; }

Extension Classes

While this handles the implementation, there is still of the question of syntax. This is where a new concept called the “extension class” comes into play. An extension class would be defined with this syntax:

public extension class MyClassExtensions : MyClass

Any method or property placed inside MyClassExtensions would automatically be treated as an extension method/property of MyClass. Visually, the members of MyClassExtensions would look no different that members declared directly in MyClass. The old syntax where you explicitly marked the method as static and included the “this” keyword would no longer ne necessary.

Fields and events declared inside an extension class would be implemented via the aforementioned ConditionalWeakTable. Properties could be explicit or automatic, with the latter again using a ConditionalWeakTable as a backing store.

Static Extensions

Another use case made possible with the extension class concept is something informally called “static extension members”. These would allow you to add static methods to a class much in the same way you can add member methods to objects.

To clarify the use of this, Erik Schierboom writes,

It would allow me [to], for example, extend the Assert class in xUnit.

Considerations

While common in dynamically typed languages, the ability to add arbitrary state to a class does seem somewhat disconcerting to some developers. HellBrick summarizes the concerns about extension fields,

It may appear natural to try that, but I'm afraid that's a feature that can easily be abused. If you want to attach an additional state to an instance of a class, I think in majority of cases it would be much better to either derive from it or to create a wrapper around it. Either way, you are explicit about your intentions. Using some automagical attached weak field references with an unclear lifetime as an alternative approach to design classes may have some appeal as a very cheap solution, but at the same time has a potential to lead to a bunch of the convoluted code that's gonna be difficult to maintain.

The counter argument is that without extension fields it isn’t possible to offer auto-properties via extension. Chrisaut continues,

Even if that were disallowed, if you still allow property setters, developers will try to come up with their own solution and half of them will get it wrong in subtle ways. For instance they may create a Dictonary<MyClass, string> and store it there. Ooops, now the instance can never be garbage collected and you created a nice leak. Most developers don't even know about weak references or ConditionalWeakTable. So if we really want to discourage attaching additional state (essentially we don't want extension fields), then I feel the only way is to explicitly only allow Property Getters, and not support Setters or even getter only auto properties. Which may be fine. But my guess is people will bump into this limitation. I'm not sure if getters only would cover all of the scenarios people want. Or if we just say it covers enough of the scenarios and the rest is not worth it.

Another problem with extension fields is the storage of disposable objects. If you store a disposable object in a ConditionalWeakTable, you’ll need to find a way to explicitly dispose it once the parent object is garbage collected. ConditionalWeakTable won’t do this for you and relying on the finalizer is risky.