C# 3.0: Now with Categories!

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#.



5 Responses to “C# 3.0: Now with Categories!”

  1. Michael Tsai - Blog - C# 3.0: Now with Categories! says:

    […] Bill Bumgarner: […]

  2. Nicholas Paldino [.NET/C# MVP] says:

    There is nothing wrong per se with the nomenclature that you suggest: public static class MyExtensions extends object {...}. However, it does introduce some issues. The first is that you are limiting the class to be nothing but extensions for object. This might be fine for some people, but too many people would not want that kind of limitation on their classes.

    Also, it should be noted that you do not need to have your class be declared as static in order to use extension methods. The only requirement for extension methods is that the method be static, and that it use the this qualifier before the type declaration in the parameter list. The static keyword in a class declaration only tells the compiler to make sure that the type has no constructors, and no instance methods.

    In the case of placing an extension method on Object in the System namespace, obviously, that’s can be a huge issue. However, it only occurs when you reference two assemblies that put the same extension method (meaning name and parameter list) in the same namespace and you use the using declaration in the file.

    In the case of a clash (either with an already existing instance method, or another extension method from another namespace), the compilation should fail, citing that the method is ambiguous (although I have to check the spec for this to be sure).

    Remember, extension methods are still static methods, so you can still call them in the standard way. They are really just compiler magic/syntactic sugar which was implemented to help make LINQ possible (querying over objects). More specifically, the IEnumerable interface is what is targeted in order to help implement this functionality (along with lambda expressions, anonymous types, and type inference, i.e. the var keyword).

    Personally, I do not like the syntax with which extension methods are called (and I have been very open with MS about this as well). I believed that at the call site, there should be a different way of calling it, something like obj..MagicDoStuff() or obj->MagicDoStuff() (I prefer the latter for what should be obvious reasons to C++ developers).

  3. Jay Tuley says:

    I’m not sure what your point about the System namespace is. For starters, declaring an extension method in System would not be appropriate EVER, also the C# 3.0 spec says that extension methods have a precedences which seem to be help with the problems you are suggestion. The first precedence states that real instance methods take precedence over extension methods — this may still cause the writer of the extension method trouble, however it’s certainly less confusing then the opposite as illustrated in your web objects example. The other precedence is that inner namespaces take precedence over outer namespace — so if someone else stupidly declares an extension method in system and it’s imported, most likely my extension method with the same name is in the namespace i’m using so it will have precedence and thus not cause me grief, but if it’s not in my namespace and is just another assembly than it’s ambiguous like Nicholas said and you’ll probably get an error saying so from the compiler or runtime and you’ll just have to reworking your use of the “using” keyword.

  4. bbum’s weblog-o-mat » Blog Archive » C# 3.0 Categories Followup says:

    […] A couple of days ago, I posted some observations and questions regarding C# 3.0’s extension methods. […]

  5. Ken Anderson says:

    Bill, that bool return addObjectIfAbsent: method was way back in EOF 2.1 circa 1996 – I know, I found it! (or at least, was one of the finders). I was working at Stratus Computer using OpenStep to build their call tracking system and it took me 3 days of stack and assembly debugging to figure it out…what a nightmare!

Leave a Reply

Line and paragraph breaks automatic.
XHTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>