Update: Go read this followup that specifically responds and expands upon the comments left on this post.
I was perusing my various random RSS feeds and ran across a C# 3.0 missive that caught my eye. (Yes, my life is not entirely about pictures and tequila — I do still do coding stuff. Lots of it.)
C# 3.0 is adding something called extension methods. With extension methods you can extend pre-existing classes with new methods.
Kinda like categories. Only better. Sort of.
In particular, extension methods can be limited to a namespace such that the additional methods are only visible to code that uses a particular namespace.
That is cool. Objective-C’s categories introduce some fairly serious fragility in that they can radically, transparently, and incorrectly modify the behavior of existing classes globally to the application. This has caused no end of problems.
One shining example. Back when WebObjects — when Enterprise Objects — was written in Objective-C, everyone added this method to NSArray:
@interface NSArray (MySuperObviousAndVeryUsefulCategory)
- (void) addObjectIfAbsent: anObject
@end
Good enough. Now, yay about WebObjects 4.5 or so, Apple added this in a category:
@interface NSArray (EORelationshipManagementUtilities)
- (BOOL) addObjectIfAbsent: anObject
@end
The BOOL return was used by the editing context to determine if a to-many relationship was really modified by the -addObjectIfAbsent:. Of course, the (void) version was overriding the (BOOL) version and, well, EOEditingContext’s relationship management was either radically slow or just flat out broken when it came to relationship management.
Much confusion ensued until someone figured out what the hell was going on. Yay Categories!
But I digress. Sort of.
By limiting the extension methods to namespaces, C# has effectively avoided the above scenario.
Or did they? As it turns out, Microsoft has given the developer the ability to avoid the problems as described above while also giving the developer an infinite spool of rope with which to tie exceedingly difficult debugging knots.
Why? Because you declare extension methods to be a part of the System namespace and, thus, said extension methods will be dragged into anything that uses the System namespace, which is pretty much everything.
It appears — I’m not 100% certain as I’m not deeply versed in C# — that you cannot override existing methods using extension methods. So, maybe the rope isn’t quite as infinite as Objective C categories, but it is still pretty damned long.
I’m sure some will think that is a horrible and obvious oversite, but my rather painful and numerous experiences of debugging category override induced bugs in large bases of Objective C code puts me quite firmly in the “wise choice” camp.
It does beg the question of what happens when you drag in two namespaces that both implement the same extension method to a particular class.
This isn’t without its problems. You still have to go and find all the bloody extension methods for any one class if you want to really know how that class is used within any given body of code. No fun. Supposedly, Visual Studio is going to solve this particular problem through features in the IDE (which, btw, VS’s integration of reference material and documentation is nothing short of amazing, even if the UI can be a bit daunting).
Which brings us to syntax. OK — so — you want to write a method that extends a class. Obvious enough, just add some little bit of magic dust to the existing class declaration to turn said declaration into an extension of an existing class. Kind of like Objective-C does:
@interface NSObject (BreakTheWorld)
- (BOOL)isEqual:(id)object;
@end
@implementation NSObject (BreakTheWorld)
- (BOOL)isEqual:(id)object { return random() % 2 ? YES : NO; }
@end
However, this is not how you would do it in C#. Not at all. Instead, you sprinkle the keyword this followed by the class to be extended into the beginning of the arguments for the method. Huh? Like this:
namespace System
{
public static class MyExtensions {
public static void MagicDoStuff(this object o) {
... do magic here ...;
}
}
}
Got that? The ‘this object’ says to extend the base ‘object’ class. The ‘namespace System’ says to do it in the System namespace. Ouch. This also means that you can apparently define extensions for multiple classes within a single class’s worth of implementation. How this interacts with the containing class itself is unclear.
Without knowing C# intimately, my off the cuff expectation would be that public static class MyExtensions extends object {...} or the like would make for a more comprehensible syntax. What would be lost by doing that?
In any case, extension methods seem like a pretty neat feature. The syntax seems a bit unfortunate.
Actually, let me a bit more blunt: What did I screw up in this analysis? There has to be a couple of things as I don’t spend my days writing C#.