58

I know from this question that extension methods can only operate on class instances, not the static class itself. This means I can't extend useful static classes like Convert and Math.

What I want to know is, why is this the case? From the link above, there are some suggestions on how the C# team could have implemented this kind of functionality. Is there some philosophical reason why it isn't supported?

For example, here's a rationale behind why there is no built-in LINQ ForEach<T> extension for IEnumerable<T>.

Community
  • 1
  • 1
Justin Morgan
  • 2,427
  • 2
  • 16
  • 19
  • The example is a rationale behind why a feature was not implemented. I was looking for a similar kind of rationale for the lack of static class extension methods, if it exists. (Edit: this was in response to a comment that was later deleted.) – Justin Morgan Feb 05 '11 at 19:34
  • 1
    Great question, hopefully you get better answers than the opinions that have been posted as answers. – Gabriel Magana Feb 05 '11 at 21:06
  • Related discussion on GitHub: https://github.com/dotnet/roslyn/issues/996. – vulcan raven Jun 18 '15 at 13:33

6 Answers6

78

the C# team could have implemented this kind of functionality. Is there some philosophical reason why it isn't supported?

There's no technical reason, and no philosophical reason. However, as I often point out, I don't have to provide a justification for not doing a feature. Features aren't cheap; they are extremely expensive and they must not only justify their own cost, they must justify the opportunity cost of not doing the hundred other features we could have done with that budget. We must justify the cost of features to our stakeholders, but we need not justify saving time and effort by not implementing features that don't meet our bar.

In particular, the proposed feature does nothing for LINQ; extension methods were added to make LINQ work. Anything that didn't make LINQ work was very hard to get into C# 3.0; we had a lot of work on the schedule and not much time to do it in. (I was surprised that automatic properties made it in.) Cutting an unnecessary feature before even designing it saved a lot of time and effort that was spent on other things that do make LINQ work.

In short: the suggested feature has never met our bar for net benefit over cost, and we've always had more important features to spend our limited time and effort on.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • That makes sense for C# 3. You mentioned that you prototyped extension properties in C# 4 but then cut the feature. Is it safe to assume that other kinds of extensions (like static class methods) didn't get the go ahead in C# 4 for the same resource budgeting reasons? – Justin Morgan Feb 06 '11 at 15:56
  • 12
    @Justin: Correct; we had designs ideas for "extension everything" -- constructors and events and the whole bit -- but if we couldn't even fit extension properties in, there wasn't much of a point. They're nice to have but the value added is not enough to justify the costs. – Eric Lippert Feb 06 '11 at 16:00
  • What drives the C# language: features-that-are-implemented or the specification? It seems like they go hand-in-hand. (I'm glad to see C# is changing, just wondering if it's the chicken or the egg first.) –  Feb 06 '11 at 18:57
  • 6
    @pst: We do the feature design and come up with a draft specification; enough to write a prototype and start doing test plans. The prototype implementation and testing shows places where the spec is deficient, so we refine the spec and the implementation accordingly. It's not so much the chicken and the egg as the architects, the builders and the residents working together to put up the building. – Eric Lippert Feb 06 '11 at 19:22
  • @Eric: Just out of curiosity, aren't "extensions everything" trivial to implement? I am not sure, but aren't they members that just appear to be actual members of some type, but in reality aren't? I am just trying to think how much it's requested and valued vs the cost of the implementation. I imagine it's not so trivial if you guys chose not to have it. – Joan Venge Feb 07 '11 at 17:52
  • 11
    @Joan: "trivial" means "I know how to do it". In that sense, sure, it's trivial; we know we can do it. But trivial features are not necessarily cheap. I know how to search the entire internet for keywords in sub-second time; the search problem is trivial in the sense that the algorithm is quite short, but the engineering problem of implementing the solution is quite expensive. There are no *cheap* compiler features. The design, implementation, testing and maintenance costs of any feature are considerable. We have to make sure the benefits are commensurate. – Eric Lippert Feb 07 '11 at 17:59
  • 2
    @Joan: Here's an interesting discussion of the meaning of "trivial": http://fishbowl.pastiche.org/2007/07/17/understanding_engineers_feasibility/ – Eric Lippert Feb 08 '11 at 08:13
  • @Eric: Thanks for posting that. It really looks useful. Before I thought trivial meant easy for everyone, as in it's easy to do for everyone, but now I know. Although I take it not every programmer uses it the way it's described in your link, right? – Joan Venge Feb 08 '11 at 17:41
  • 8
    This one is really funny: non-trivial = "It means impossible. Since no engineer is going to admit something is impossible, they use this word instead. When an engineer says something is 'non-trivial,' it's the equivalent of an airline pilot calmly telling you that you might encounter 'just a bit of turbulence' as he flies you into a cat 5 hurricane." – Joan Venge Feb 08 '11 at 18:23
13

After reading through the answers, as well as some related questions, I've assembled my understanding of the issue here.

How extension methods work

First, it's important to realize that extensions are just syntactic sugar for static methods.

// Say you have an extension method that looks like this:
class Extensions
{
    public static void Extend(this SomeClass foo) {}
}

// Here's how you call it
SomeClass myClass;
myClass.Extend();

// The compiler converts it to this:
Extensions.Extend(myClass);

The method doesn't actually become part of the class. This is why you can't access private members from an extension method. Extension methods change C# syntax only, and do not violate the concept of OOP accessibility. In fact, if you write an extension method and a normal static method that do the same thing, then decompile the MSIL, they are exactly the same.

Why extension methods exist

So if they don't add actual functionality, why have extension methods at all? The answer is LINQ:

// LINQ makes this easy to read
array.Where(i => i&1 == 0).Select(i => i*i);

// Without extension methods, we would have to do it like this
Enumerable.Select(Enumerable.Where(array, i => i&1 == 0), i => i*i);

In a way, all of LINQ is just syntactic sugar, since everything it can do could be written in a clunky, non LINQy way. Obviously the C# team felt that the readability gained by LINQ was worth it, but it begs the question, "why did they stop there?"

Why not other extension types?

Eric Lippert, one of the C# compiler devs, described in a blog post that a huge part of C# 3 was creating all of the constructs necessary for LINQ: "implicitly typed locals, anonymous types, lambda expressions, extension methods, object and collection initializers, query comprehensions, expression trees, [and] improved method type inference." Because the C# team was the most resource-limited team for the 2008 .NET release, additional types of extensions that weren't strictly necessary for LINQ were not included.

The team did consider implementing extension properties in C# 4, and actually wrote a working prototype, but it was dropped when they discovered it would not enable the WPF team as implemented (which was one of the motivators for the feature). Eric Lipper later said they they did consider extension methods for static classes, but could not justify the real-world benefits against the costs of implementation, testing, and maintenance.

A workaround

It is possible to write an extension method that gets close:

public static TResult DoSomething<TType, TResult>(this TType @class)
    {
        // access static methods with System.Reflection
        return default(TResult);
    }

// This works, but poorly
typeof(Math).DoSomething();
typeof(Convert).DoSomething();

But it's pretty ugly. It requires reflection, and can't support any kind of intelligent typing, since any Type can call it and that's likely not the intended functionality.

Community
  • 1
  • 1
Justin Morgan
  • 2,427
  • 2
  • 16
  • 19
3

Extension methods operate on objects, not classes. If you want an extension method to operate on a class, I suppose you could do this:

public static T DoSomething<T>(this T @class) 
    where T:Type
{
    // access static methods via reflection...
    return @class;
}
kelloti
  • 8,705
  • 5
  • 46
  • 82
  • this would work for `typeof(Math).DoSomething()` but not for `Math.DoSomething()` – Mark Cidade Feb 05 '11 at 19:45
  • @Snobear - class is a keyword, so it's invalid syntax to use it as a variable name. @Mark Cidade - I suppose you're right, but it would still work. As all the other answers hint at, its kind of an awkward thing to do anyway... – kelloti Feb 05 '11 at 19:48
  • 7
    you could use @class for that. – Femaref Feb 05 '11 at 19:52
  • 1
    @kelloti, I would extend Femaref's answer - you SHOULD use @class for that – Snowbear Feb 05 '11 at 19:56
  • Ah! interesting. I may use that sometime. Is `@` a valid identifier character in C#? – kelloti Feb 05 '11 at 19:57
  • @kelloti, it is only valid to have one `@` at the beginning (first character) of variable name (probably other names too). So it cannot be used exactly as alphanumeric character but it can help you to name you variable as `@class` – Snowbear Feb 05 '11 at 20:10
3

I believe the answer for your question is because it doesn't make any sense to extend with static methods. Main reason behind introducing Extension methods is not extending class which you do not own. Main reason is that it will allow reduce methods nesting in examples like these:

 Enumerable.Select(Enumerable.Where(arr, i => i & 1 == 0), i => i*i); // not the best thing I ever read

 arr.Where(i => i&1 == 0).Select(i => i*i); // wow, I see! These are squares for odd numbers

That is why there is no such point to have extension methods for static classes.

Snowbear
  • 16,924
  • 3
  • 43
  • 67
  • 6
    I've come across multiple times where I wanted to add a method to a static class. For example, I wanted to add `Path.Combine(params string[])` and `Path.GetMimeType(string path)`. – Daniel T. Feb 05 '11 at 19:59
  • 10
    @Daniel, Regular static methods should be fine for that. It is not that vital to pretend that these methods belong to `System.IO.Path` class. I would even insist that it is a bad desicion. Create your own simple static class if you need such thing. – Snowbear Feb 05 '11 at 20:01
  • 2
    Right answer. I would also add that you can use OOD principles such as aggregation and/or other design patterns such as decorator in order to accomplish this and still offer very readable and maintainable code. – SRM Feb 05 '11 at 20:08
  • 3
    @SRM, Snowbear: same could be said about regular extension methods, and yet they are incredibly useful – BrokenGlass Feb 05 '11 at 21:50
2

Static extensions are possible in F# :

type Platform = 
    | Win32
    | X64
    override this.ToString() = 
        match FSharpValue.GetUnionFields(this, typeof<Platform>) with
        | case, _ -> case.Name.Replace('X', 'x')

type Environment with
    static member Platform =
        if System.IntPtr.Size = 8 then Platform.X64 else Platform.Win32
ded'
  • 664
  • 4
  • 11
1

Well its not only about not implemented, but it will cause confusion of where the method belongs to? Static extensions were introduced because linq came in later version and only to support linq in easy to code way, static extensions are useful.

Static extensions are useful only to make code more readable. It has no significance at runtime or compile time.

Akash Kava
  • 39,066
  • 20
  • 121
  • 167