C# 3.0 Categories Followup

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

There were two comments (and a link back) posted and both were extremely interesting. So, first, thanks to Nicholas Paldino and Jay Tuley for taking the time to respond and do so with such detail.

Some clarification on how extension methods work (and how they different from categories).

Given this context:

namespace System
{
  public static class MyExtensions {
     public static void MagicDoStuff(this object o) {
            ... do magic here ...;
        }
    }
}

The static in the class declaration simply prevents instantiation of the class. The C# docs recommend that static classes are useful for containing methods that aren’t otherwise associated with a particular class.

In Objective-C, the developer would typically just declare a set of standard C functions to encapsulate non-class specific functionality.

Declaring extension methods — or even a class — in the System namespace is discouraged. The resolution of extension methods are one hell of a lot less fragile than the resolution of methods in Objective-C categories.

First, extension methods cannot override existing functionality (that I know of). Secondly, if two extension methods of the same name and arg list are compiled into the same namespace, the compiler should barf up an ambiguous method error.

What happens with dynamically loaded code? OK — more basic question that demonstrates my ignorance — what dynamic loading features does C# even support? Hell, what level of support does C# have for dynamic resolution and/or binding of methods?

On the Objective-C front, loading a category is pretty much a complete crap shoot. You can blow away pre-existing methods. Loading multiple categories with the same methods on the same class yields somewhat random results as to which method wins. Signatures — the types of the arguments of the method — is not considered.

Not that it is the Wrong Way. It is just the C Way. Though one must wonder if Objective-C should add a handful of tools for verifying edits to the runtime for potential stupidity. Shouldn’t be hard to do.

Pretty much everyone I have interacted with, including some .NET professionals, don’t like the syntax. The magic this at the beginning of the arg list is just too damned subtle.


Now, I want to respond to Jay’s comments directly:

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…

Yes. Everything I have read or heard is that declaring extension methods in the System namespace is a Very Bad Thing To Do.

And I completely agree.

However, something similar was said about categories around the time the Foundation was introduced (mostly because folks hadn’t fully figured out the implications in the Object days).

And, yet, people adding categories to NSObject — the base class — left and right. And conflicts ensued. And hard to debug problems cropped up. And people thought of better ways to do things that ignored internal implementation details (which, thankfully, isn’t something C#’s extension methods will directly suffer from).

C# has the advantage of starting with a strong statement about adding crap to the system namespace. But I’d bet a beer or two that people will add extension methods to System and that it will be a maintenance headache down the road. Nowhere near as bad as categories, but it is still going to hurt.

…, 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.

OK — now that is very interesting. While it will prevent extension methods from boning the functionality of existing classes — something Objective-C categories completely fail at — it’ll definitely cause problems for the authors of extension methods (as Jay states).

In particular, if an extension method writer picks a particularly obvious name for a method and the class later adds the same method, all hell will break loose. Or will it? Depending on the binding semantics of C#, maybe the compiler will catch this before it makes it into running code? What about dynamic loading (there is that question again)?

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.

Nice — as much as the syntax is kinda barfomatic, it is clear that the designers gave a lot of thought to avoiding fragility as codebases evolve and as the underlying libraries evolve.

And, yes, the webobjects example was completely broken. Cost the industry 10s of thousands of dollars to fix. Hell, I probably billed north of $3,500 to find and fix that particular problem for the clients that my company had at the time.



9 Responses to “C# 3.0 Categories Followup”

  1. bbum’s weblog-o-mat » Blog Archive » C# 3.0: Now with Categories! says:

    […] Update: Go read this followup that specifically responds and expands upon the comments left on this post. […]

  2. Jim says:

    Does the current documentation offer any particular advice about the use/abuse of categories on system provided classes?

  3. Ben Lings says:

    My impression (and this is only from reading the descriptions in various places on the web—I haven’t compiled anything and then looked at the IL to verify it) is that the extension methods are resolved only at compile-time. They are also resolved based only on the declared type of the variable.

    Using your example:


    object obj = new object();
    obj.MagicDoStuff();

    gets compiled to


    object obj = new object();
    MagicDoStuff(obj);

    It’s syntactic sugar which I guess was put in mainly to support LINQ. Without it, the syntax for LINQ would be horrible. (Given that all the System.Query methods couldn’t be added to the IEnumerable<T> interface without some serious breakage…)

  4. Mont Rothstein says:

    OK, I can’t take it anymore. You keep harshing on the ability for categories to override existing methods.

    I agree that this can cause problems (obviously) but the power to fix bugs in frameworks provided by someone else (cough apple cough) should not be trivialized. The amount time time I have saved because I could fix a bug by using categories is significantly greater than the time I have spent hunting down category related bugs.

    That said, it would be nice if you had to explicitly say when you wanted to override an existing method. You could prefix the method with something like override_stupid_borken_method_so_things_work_like_they_should, or even just override. Without the keyword it would go boom, or simply not load the category.

    C# has an override keyword, unfortunately the original method has to be declared virtual which is not the default and therefore doesn’t get used much.

    On a different note I find it sad that it took LINQ to get MS to see the use of categories, and then only begrudgingly.

    -Mont

  5. Jay Tuley says:

    OMG, Duh, Ben you are so right. C# 3.0 uses the .net 2.0 runtime, there are no runtime changes, this is all done at compile time, which would negate any problems with dynamically loaded code changing what methods run after the compiling. The spec was worded that this was done during the “processing of the invocation” however reading again more closely it meant “processing of the invocation [syntax]”.

  6. Jay Tuley says:

    What happens with dynamically loaded code?

    The extension method is definitely getting resolved at compile time, it’s not going to make a difference with dynamically loading code adding new extension methods (Unless maybe if you have the same method extension in two different assemblies with the same namespace like System in static classes with the same names :-P, which would cause a runtime error on load because of the class with the same name and namespace)

    OK — more basic question that demonstrates my ignorance — what dynamic loading features does C# even support?

    Libraries, modules, and you can emit new classes programatically too.

    Hell, what level of support does C# have for dynamic resolution and/or binding of methods?

    Most of the time you are not dynamically resolving methods, there is some runtime resolving for methods obviously for virtual methods and protocols (err… interfaces) only what you would need for polymorphism. Also while it’s not a language feature, .net does have the ability to dynamically query methods via the reflection api and then invoke them, it’s big and ugly, but it does allow one to write classes that use dynamic bindings. The api query can be much more explicit than cocoa’s, but of course there’s a lot more meta data in c# than objective-c to query by too.

    C# is statically and strongly typed, but they do make it possible to do things you expect in dynamic languages, they can even do distributed objects with proxies, the only catch is that you have to have a protocol (err…interface) for that object, but you just feed it to an api and it’s emits a proxy class that inherits the protocol on the fly and it will talk on the other end to an object that invokes methods dynamically by reflection off your remote object.

  7. Tim Wood says:

    I’ve have to 2nd Mont Rothstein’s comment about fixing bugs in busted frameworks. This has saved us tons of time, though we’ve gotten to the point that we don’t do this so much with categories anymore but with runtime method replacement (OBReplaceMethod*), possibly with a check for an AppKit/Foundation version number to avoid the replacement in newer OS versions.

    Also, I really think categories/extensions in the System namespace are *good*, especially if you get link-time conflict checking.

    We frequently define default implementations of functionality on NSObject and then specialize where necessary. I really dislike code like this:

    if ([object respondsToSelector:@selector(foo)])
    [object foo];
    else {
    … default logic …
    }

    It’s slower, more verbose, more fragile and worst, ugly! Instead of this, I’ll put the default logic on NSObject, skip the respondsToSelector: and just call -foo straight up.

    If someone has a better pattern, I’m open to suggestions, but this has served us well and with link time conflict checking it’d be even better (actually, I have a little command line tool that uses our OmniMachO framework to check for method override conflicts at runtime, but that requires me to actually invoke it… 🙂

  8. leeg says:

    @Tim: according to this article (a.k.a. I knew about this but am covering my butt), "ObjC 2"[*] has optional methods in protocols. Now if NSObject were to implement a method -(BOOL)conformsEntirelyToProtocol:(Protocol *)p then you could eschew the checks at method-call-time by just checking that you could do everything upfront. Such a method could be added by, um, a category on NSObject ;-).

    [*]I dislike that marketecture phrase…wasn’t adding categories, or protocols, or language-level exceptions, a reversion of Objective-C?

  9. Jonathan Leonard says:

    Regarding dynamic invocation (or late-time binding): MS is getting ready to make a friendlier syntax for this than those already mentioned in this thread:

    See:
    http://blogs.msdn.com/charlie/archive/2008/01/25/future-focus.aspx

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>