I know this is a bit off topic, but I just had to comment about it here.

Since I was not able to attend to PDC I’ve been hearing a lot about the announcements and new stuff coming out of it.

One of these things is some of the features in C# 3.0 spec called “Extension Methods”.
In short, Extension Methods allows you to extend an existing class that is sealed by creating a class that has a static function with a special syntax which allows you to call that function on an instance of that object as if it was a member function.

For example, if I want to extend the the String class (which, as you all know, is a sealed class and has its reasons to be like that which I won’t get into at the moment) I would create a class like this:

public static class MyExtension {
public static void Foo(this string s) {
Console.WriteLine(“MyExtension::Foo = {0}”, s);
}
}

Notice the “this” keyword near the function’s parameter. This is the keyword that does the magic.

To invoke this method I would use the following syntax:

string s = “Testing Extensions”;
s.Foo();

You see, it looks like the String class always had a method named Foo.

Now as much as some people think this feature is cool I don’t see the point of actually having it and showing it like a big thing.

This “functionality” could have been used in previous version of C#. In previous version all you had to do is this:

public static class MyExtension {
public static void Foo(string s) {
Console.WriteLine(“MyExtension::Foo = {0}”, s);
}
}

and invoke the code like this:

string s = “Testing Extensions”;
MyExtension.Foo(s);

Which, in my opinion, is a lot clearer when reading this code since I know that there is a class somewhere in the code (either from a reference or inside the same .cs file or in another .cs file in the project) that is called MyExtension and have a method called Foo.

So why do we need this functionality?

I guess we don’t, but the only reason I can think of came to me from seeing all the examples out there which involves the String class. Perhaps because the String class (and a few other classes in the Base Class Library (BCL)) are sealed, some people whined too much about it and someone decided on adding this to make them get the same feel as if they inherited from a sealed class and extended it.

Now I know that the C# Spec is saying: “Extension methods are less discoverable and more limited in functionality than instance methods. For those reasons, it is recommended that extension methods be used sparingly and only in situations where instance methods are not feasible or possible.”

But what I do know is that every feature which is not very specific and limited and can be used in a wrongful way, WILL be used in a wrongful way.

So the main question here is, why add such a feature if there are a LOT of other features that are more needed and can get a enchance productivity?

(I wonder how can one get into the language design meetings or at least for the review process of the specs before they are submitted 😉 ).

  • I agree this is somewhat dirty and has a distinct JavaScript smell, one not befitting C#…

    A cleaner way to do it, and one just as clear, would have been to allow creation of special “extension” classes, which basically extend the syntax of an existing class (sealed or not) in a namespaced way.

    The resulting syntax would be:

    s.MyExtension.Foo();

    You get the benefit of being able to extend the class while clearly marking the extension as one, and the side-benefit of preventing naming clashes (now or in the future) by using a namespace.

  • Len Holgate

    Eran,

    I agree with you. It’s a hacky feature and it’s bound to lead to confusion and become a feature that’s banned by local coding standars…

  • While I disagree with Yaniv about not needing to use this feature at all, there are a few things that do make sense.

    The ability to have a better context for your functions (as Yaniv put it in a conversation we had) is nice but I still think the benefits do not out weigh the problems this can make.

    Yaniv’s syntax suggestion also handles a few things that I think are not being handled in the C# 3.0 spec which is namespacing the extension methods so that if you have more than one extension method with the same name it won’t cause a resolution problem.

    This is something that I don’t think was addressd in the spec (though I haven’t had time to dig inside of it and see if this scenario is being handled).

  • Eyal

    I agree that this can lead to some confusion, but I still like this feature.
    I have quite a lot of utility classes for strings (i.e. MD5Hash) and streams (i.e. StreamCopy) and it would be much more elegant to write s.MD5Hash() than StringUtils.MD5Hash(S), or fileStream.CopyTo(memStream) than StreamUtils.Copy(fileStream,memStream).
    This would also help in porting – let’s say i’m using a Windows Form Grid that has a Grid.Clear() method. I now purchase a new Grid and want to port my code as quickly as possible. If my new grid doesn’t have this method, all i need is to extend it with a Clear method and voila! my code still compiles.
    By the way – Delphi 2005 introduced a feature called class helpers (http://bdn.borland.com/article/borcon/files/3286/slides/3286_files/frame.htm#slide0052.htm) exactly for this reason. it allowed easier porting of win32 Delphi code to .net code.

  • Barry Kelly

    It is absolutely required in order to extend IEnumerable<T> to support the new query methods such as Max, Select etc.

    There’s a type in the System.Query namespace which extends IEnumerable<T> with the relevant methods. It’s what allows the select, order by, etc. and all the other neat LINQ syntax to interoperate with all the existing IEnumerable<T> implementors.

  • The need is clear, but as is usually the case, there is more than one way to skin the syntax cat.

    Barry said: “It is absolutely required in order to extend IEnumerable*T* to support the new query methods such as Max, Select etc”. While it may be necessary to add the Max Select methods functionality, I don’t see why it’d matter, from a functionality as well as ease of use point of views, if these methods were “namespaced”:

    MyEnum.LINQ.Select()

    instead

    of MyEnum.LINQ

    When designing a language, “absolutely required” must be weighted against questions like “What happens in the next rev of the class library”.

    Let’s assume I took advantage of this new functionality and added my own (non-namespaced) s.MDHash(). Let’s assume that the powers that be at the MS string division really like this and decide to add their own string.MDHash() function in the next rev, which works almost but not quite like Eyal’s MDHash(). What happens then to Eyal’s code? It compiles perfectly, but could go wrong in many subtle and scary ways at runtime. The risk for this is too significant to ignore without providing a solution. Of course the solution could be retro-fitted on the next rev (migration tools that scan your code for “dangerous” stuff before moving to the new rev), but why create a problem and then deal with it instead of avoiding it altogether?

  • Following another conversation with Eran, and taking into account the fact that it’s not just extending classes but also allowing the compiler to handle fragements in other syntaxes (SQL..), I could imagine a somewhat different approach.

    The problem definition: need to integrate into the main language functionality that is better defined in another language.

    Concrete example: when writing in C#, it might be good to process the data using SQL syntax.

    Another example: when writing in C#, a certain functionality may be better expressed using PERL syntax…

    So, a somewhat different shot at the syntax (adapted from the example at http://msdn.microsoft.com/vcsharp/future/linqsamples/restriction/default.aspx#simple1:

    public void Linq1() {
    int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

    var lowNums = Syntaxes.Query.SQL (numbers) {
    from n in numbers
    where n < 5
    select n;
    }

    Console.WriteLine(“Numbers < 5:");
    foreach (var x in lowNums) {
    Console.WriteLine(x);
    }
    }

    Yes, somewhat longer, but a lot more powerful, and a lot more safe…

    What if actually the best way to process the numbers data is in PERL and not SQL?

    public void Linq1() {
    int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

    var lowNums = Syntaxes.Languages.PERL (numbers) {
    // PERL code here to process the numbers array and return a result
    }

    Console.WriteLine(“Numbers < 5:");
    foreach (var x in lowNums) {
    Console.WriteLine(x);
    }
    }

    Of course, this can be mixed and matched with my previous suggestion for extending a class syntax in a namespaced way:

    public void Linq1() {
    int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

    var lowNums = numbers.myNamespace.myFunc();

    Console.WriteLine(“Numbers < 5:");
    foreach (var x in lowNums) {
    Console.WriteLine(x);
    }
    }

    where myNamespace.myFunc() is defined as

    classExtension myNamespace
    extends int[]
    {
    int myFunc(int[] arr) {
    return Syntaxes.Languages.PERL (arr) {
    // PERL code here to process the numbers array and return a result
    }
    }

  • Eyal

    regarding namespace clashes: I don’t think this is a real problem. Well – I don’t think there’s a new problem here. I could have defined a class named MyGreatMD5Class in my own namespace and it still would clash if micorosoft added a class like that in one of their own namespaces. Still – it would clash only when “using” both namespaces. My guess is that you’ll have to “use” the namespace where you add extension methods in order to use them in the short syntax. so the problem remains the same as the old using\namespace clashes problem. If you won’t have to add a using clause to your namespace and still be able to use extension methods – then this is a real problem.

  • Pingback: [OT] C# 3.0’s Extension Methods - Again » Advanced .NET Debugging()