Share via


Why Doesn't C# Implement "Top Level" Methods?

C# requires that every method be in some class, even if it is a static method in a static class in the global namespace. Other languages allow "top level" functions. A recent stackoverflow post asks why that is.

I am asked "why doesn't C# implement feature X?" all the time. The answer is always the same: because no one ever designed, specified, implemented, tested, documented and shipped that feature. All six of those things are necessary to make a feature happen. All of them cost huge amounts of time, effort and money. Features are not cheap, and we try very hard to make sure that we are only shipping those features which give the best possible benefits to our users given our constrained time, effort and money budgets.

I understand that such a general answer probably does not address the specific question.

In this particular case, the clear user benefit was in the past not large enough to justify the complications to the language which would ensue. By restricting how different language entities nest inside each other we (1) restrict legal programs to be in a common, easily understood style, and (2) make it possible to define "identifier lookup" rules which are comprehensible, specifiable, implementable, testable and documentable.

By restricting method bodies to always be inside a struct or class, we make it easier to reason about the meaning of an unqualified identifier used in an invocation context; such a thing is always an invocable member of the current type (or a base type). 

Now, JScript.NET has this feature. (And in fact, JScript.NET goes even further; you can have program statements "at the top level" too.) A reasonable question is "why is this feature good for JScript but bad for C#?"

First off, I reject the premise that the feature is "bad" for C#. The feature might well be good for C#, just not good enough compared to its costs (and to the opportunity cost of doing that feature instead of a more valuable feature.) The feature might become good enough for C# if its costs are lowered, or if the compelling benefit to customers becomes higher.

Second, the question assumes that the feature is good for JScript.NET. Why is it good for JScript.NET?

It's good for JScript.NET because JScript.NET was designed to be a "scripty" language as well as a "large-scale development" language. "JScript classic"'s original design as a scripting language requires that "a one-line program actually be one line". If your intention is to make a language that allows for rapid development of short, simple scripts by novice developers then you want to minimize the amount of "ritual incantations" that must happen in every program. In JScript you do not want to have to start with a bunch of using clauses and define a class and then put stuff in the class and have a Main routine and blah blah blah, all this ritual just to get Hello World running.

C# was designed to be a large-scale application development language geared towards pro devs from day one; it was never intended to be a scripting language. It's design therefore encourages enforcing the immediate organization of even small chunks of code into components. C# is a component-oriented language. We therefore want to encourage programming in a component-based style and discourage features that work against that style.

This is changing. "REPL" languages like F#, long popular in academia, are increasing in popularity in industry. There's a renewed interest in "scripty" application programmability via tools like Visual Studio Tools for Applications. These forces cause us to re-evaluate whether "a one line program is one line" is a sensible goal for hypothetical future versions of C#. Hitherto it has been an explicit non-goal of the language design.

(As always, whenever I discuss the hypothetical "next version of C#", keep in mind that we have not announced any next version, that it might never happen, and that it is utterly premature to think about feature sets or schedules. All speculation about future versions of unannounced products should be taken as "for entertainment purposes only" musings, not as promises about future offerings.)

We are therefore considering adding this feature to a hypothetical future version of C#, in order to better support "scripty" scenarios and REPL evaluation. When the existence of powerful new tools is predicated upon the existence of language features, that is points towards getting the language features done.

UPDATE: More thoughts on considerations motivating this potential change here.

Comments

  • Anonymous
    June 22, 2009
    That's very good news (not that it's officially news! I get that! But if it ever does happen it would be great).

  • Anonymous
    June 22, 2009
    The comment has been removed

  • Anonymous
    June 22, 2009
    The comment has been removed

  • Anonymous
    June 22, 2009
    The comment has been removed

  • Anonymous
    June 22, 2009
    Strongly agree with TheCPUWizard. If people want or need a scripty version of C# (which is completely understandable), let them use F# or VB. You are slowly turning C#  into C++ in terms of being overcomplicated with a thousand ways to shoot yourself in the foot. You hit the sweet spot (ease of use / power / readability) with C# 2.0.

  • Anonymous
    June 22, 2009
    The comment has been removed

  • Anonymous
    June 22, 2009
    The comment has been removed

  • Anonymous
    June 22, 2009
    The comment has been removed

  • Anonymous
    June 22, 2009
    The comment has been removed

  • Anonymous
    June 22, 2009
    The comment has been removed

  • Anonymous
    June 22, 2009
    The comment has been removed

  • Anonymous
    June 22, 2009
    Yuck! My reasons have all been given by others in previous posts.

  • Anonymous
    June 22, 2009
    The comment has been removed

  • Anonymous
    June 22, 2009
    The comment has been removed

  • Anonymous
    June 22, 2009
    "We are therefore considering adding this feature to a hypothetical future version of C#," Was that hypothetical, or a real consideration? In all honesty, the fact C# enforced method encapsulation inside classes is one of the things I like the most about it. C# is relatively "clean, pure, and simple" compared other languages - please keep it that way. Isn't that why Visual Studio contains other languages? I'd hate for the C# language to become a cluttered, horrible mess just for the sake of "progress". Strong vote against this change!

  • Anonymous
    June 22, 2009
    I just want to echo some of the sentiments here.  The benefits of scripting languages have been proven with Ericsson's derivation of Haskell and CCP's usage of Stackless Python in Eve Online.  But in both cases, however, a part of that sucess is because those languages were developed, from the get go, to be scripting languages.  C# is more along the lines of C++, Eiffel, and Java.  It's better to keep the original design that has been through 4 iterations then to try and change it.  If Microsoft wants to enter into the REPL language market, it would be better to devote reources to F# or just create .NET bindings for Haskell.

  • Anonymous
    June 22, 2009
    I guess I'm just joining the chorus, but --- please don't do this to C#. Strongly agree with the CPU wizzard, please don't make C# something its not, make it easier to interop with everything else.  (Just like the dynamic keyword makes it easy to call into dynamic languages without surrendering and becomming dynamic ourselves.) Now that there are actually several genuinely distinct .NET languages out there, the next innovation needs to be in the project system.  I would like to be able to include a F# class in my C# project, or even an F# method in my c# class.  Assemblies can't have circular dependencies, so just having two projects doesn't make it easy enough to elect a different language for a single class, I might have to re-factor my entire project structure to get all the dependencies to all the right classes with no cycles in the graph. I think that the pressure to make C# more like everyone else would be a lot less if I could easially decide "language X is the most natural expression of this concept" and go from there.

  • Anonymous
    June 22, 2009
    I agree with pmnieav that this is the singular distinction between static classes and namespaces, and that putting Math. in front of Sin, Cos, Abs, etc is just silly.  I don't think the answer is free functions. Provide a "static using" directive to introduce static members and subtypes of any class into the name resolution search space, make it be usable within methods as well as within types (don't need it file-wide), and there would no longer be any need for namespaces as a separate concept. Namespaces as a separate concept are dangerous anyway.  They don't respect visibility (can't be declared internal, stretch across assembly boundaries) and don't provide anything static classes don't, except at the level of syntactic sugar (the namespace name is optional when a using directive is in effect). The significant advantages of "static using" are:

  • it improves access to existing libraries
  • namespace pollution is explicitly opt-in, unlike free methods and to a greater degree than extension methods
  • visibility
  • generics Oh, I would also require a fully qualified name with "static using", no combinations with either "using" or earlier "static using" directives. And haven't we had this discussion before?
  • Anonymous
    June 22, 2009
    I do not see a benefit even in static using, given that we have extension methods. Forced static imports of modules is one of the reasons I do not use VB anymore. I think there is much more important stuff that can make people's lifes simpler, "yield foreach" is one (small) example. Allowing global level methods, on the other hand, gives nothing a tool can't give just by transparently wrapping code int o a class. For example, when you create C# extension functions in XSLT, you do not have to create a class, XSLT compiler does that for you.

  • Anonymous
    June 22, 2009
    The comment has been removed

  • Anonymous
    June 22, 2009
    It feels like you're about to Jump the Shark...

  • Anonymous
    June 22, 2009
    I like the idea of "static using" as described by Ben. I find that more desirable than having the functions themselves be free. That would solve the Maths.Sin problem without requiring any new maths API. I realise that I really don't object to the fact that the static methods live inside a static class, I just object to having maths-related code cluttered up with "Maths." all over the place. I don't see how extension methods reduce the marginal value of "static using". I really don't think "x.Sin()" is an improvement over "Maths.Sin(x)", and the idea of "y.Atan2(x)" is terrifying.

  • Anonymous
    June 22, 2009
    I agree with the majority of previous responders in my hope that this doesn't advance into positive points for implementation; and while I'm at it would second Stefan's interest in metaprogramming.

  • Anonymous
    June 22, 2009
    @ShadowChaser "Isn't that why Visual Studio contains other languages? I'd hate for the C# language to become a cluttered, horrible mess just for the sake of 'progress'. Strong vote against this change!" I completely agree with you ShadowChaser.  C# is good at being C#, there's no reason to try to make it do all of these things that other languages do.  It will only lead to the language becoming cluttered (much like VB). I don't want C# to become like VB or JScript or any other language.  I'd use a different language if I needed a feature that desperately, that's why other languages exist. I'm not saying that the C# team should stop developing new features, I'd just like them do develop features that naturally fit in and are "in keeping" with the language.  This would keep the language consistent and the features intuitive.

  • Anonymous
    June 22, 2009
    Interesting Finds: June 23, 2009

  • Anonymous
    June 23, 2009
    I really can't see this as a good thing. And I'll be very disapointed if I see this feature in a future version of C#. I don't want to have to tame my developers to don't do a completly mess again, as they did in the Delphi era. Top level functions leads to a completly messy and almost unmanageable/understandable code. If one wants a scripty language, choose another one, not C#.

  • Anonymous
    June 23, 2009
    While I do not strongly disagree with adding this feature to the language, I don't see a big benefit for it either. In my projects I would not use this feature, because I like to use (static) classes to organize my "global" functions. I usually have no problem coming up with a name for these classes, since you can organize almost any function with a simple Noun.Verb-scheme. This has the benefit, that I can see at the call site which class (and file, see below) the function belongs to and can quickly navigate there. It is also a matter of organizing the project. Usually I have one file per class. With global methods, would I put all methods in one big Global.cs, or even one function per file? So for any larger project, I don't really see how this would help me. And making the language more complex, with no (or minimal) benefit is always bad. If you need to add something to support "global" functions, maybe extending the using clause to let us import static members of static classes would be an alternative, although I wouldn't use that feature in my projects either. The only real use case for this feature in my eyes is for embedding C# as a scripting language in other applications. I have done this a few times and you can do a lot with a combination of sub classing and adding class constructs and using-statements automatically to the code the user entered. But this is of course fragile and I can see a real benefit here. However for these scenarios, it would be even more useful to me if we had an extensible compiler pipeline like boo, so that we can modify the compiler itself for the specific scenario we need to support. I believe I've read somewhere that the compiler team is already working on something like this while they're also rewriting the C# compiler in C#. Do you have any more information on this Eric?

  • Anonymous
    June 23, 2009
    The comment has been removed

  • Anonymous
    June 23, 2009
    Another interesting post by the great Eric Lippert .

  • Anonymous
    June 23, 2009
    Another interesting post by the great Eric Lippert .

  • Anonymous
    June 23, 2009
    C# doesn't need top-level functions, though I do occasionally miss them. What creeps me out here is (if I understand you correctly) the prospect of anything remotely REPL-ish going into C#. You don't bolt a bathtub onto a motorcycle just because somebody, somewhere, might need a place to raise rabbits. Look at C++. Just, just LOOK at C++. I love C++, but holy Toledo, it's not a design to emulate. People spend years thinking in C++ all day every day and still get blind-sided by weird stuff. Language design should to some degree proceed by accretion and trial and error, just like the design of any tool. It'll never be an exact science and there'll always be room for improvement. But you have to beware of the point where you evolve a hammer into a circular saw/screwdriver, and then your wife wants a nail clipper, and you start thinking you should do it by adding more features to your circularhammersawdriver, just because you've put so much time into it already. Sorry, that reads like Spolsky on a caffeine jag...

  • Anonymous
    June 23, 2009
    The comment has been removed

  • Anonymous
    June 23, 2009
    Wow! I would never have thought that a rather innocent suggestion of adding top-level functions to the language could evolve into a flamefest worse than the one over "dynamic". It's interesting to observe where the priorities of C# coder lie, but that's quite unexpected to me. Go figure...

  • Anonymous
    June 23, 2009
    >>"Furthermore, this restricton prevents the use of static methods directly, without prefixing them with a (logically unrelated) class name. Both Java and VB.NET allow this" > Not to suggest that this would be relevant anyway (the "language X does it, so C# should" argument is flawed to start with), but it's not true that Java allows this.  You can import a class from a package, or even all classes from a package, but you still need to provide the class name when referencing the class or members. While agreeing that Java's semantics are irrelevant to C#, note that 'import static' appeared in Java SE 5.0, see http://java.sun.com/j2se/1.5.0/docs/guide/language/static-import.html.

  • Anonymous
    June 23, 2009
    "flamefest"? Huh? I'd say of people stating "more emphatic" opinions one way or the other, you've essentially got parity.  And many responses (including mine) are more along the lines of "it could be marginally useful, but there are much better things the language designers could be doing with their time". Come on...I'm sorry not everyone agrees with you 100% on the matter, but to characterize the comments as a "flamefest" is absurd.  It's been a very civil discussion, and if this is a worse disagreement than that which occurred with "dynamic", I'd say "dynamic" must've gotten a pretty warm reception.

  • Anonymous
    June 23, 2009
    "note that 'import static' appeared in Java SE 5.0, see http://java.sun.com/j2se/1.5.0/docs/guide/language/static-import.html." Ah.  That'll teach me to stay up-to-date.  :)  Thanks. Still, I note even in that reference, they strongly advocate avoiding the use of the construct except in very narrow circumstances.  I'd say in the "should this feature exist" point system, a feature that requires a warning like that would probably start off with -200 points instead of the usual -100.  :)

  • Anonymous
    June 23, 2009
    +1 for not doing it. Especially not just because it is "trendy". Trends are often overrated and don't necessarily last for long. I'd say if you want a truely competitive REPL or scripting language you'll need several other important changes to the language. If that's your goal, maybe it would be best to have a new language spinoff, say CScript? Although I love my C#3 being so much more expressive and productive (esp. thanks to the LINQ APIs and the lambdas), I can't help but think it is headed toward the bad direction. Each iteration of the language becomes more complex, soon too complex. Extensions method surely aren't good OO-design, it is my understanding that they are mostly there to make the LINQ machinery work. Anders had explained why he ruled out default parameters from C#, now they are finally there in C#4... If you need ideas for next version, here's one, which even happens to be trendy: Make asynchronous continuations easier. You need to write so much unreadable code, just to call a webservice asynchronously and handle its response (webservice being just an example). F# "async" workflows are pure genius and I'm very jealous for not having something similar in C#. As someone suggested, if languages were better integrated in Visual Studio (i.e. one could write files in different languages in the same project) that would be awesome.

  • Anonymous
    June 23, 2009
    Why do I always hear C# has such a constrained budget and that future versions may or may not happen? This has been a recurring theme in your posts. Is C# gonna be axed soon?

  • Anonymous
    June 23, 2009
    Todd, EVERYTHING has a constrained budget, and I think the development team at Microsoft (not just C# but the entire division) has been very open about the circumstances.

  • Anonymous
    June 23, 2009
    The comment has been removed

  • Anonymous
    June 23, 2009
    Oh and I have to suggest a name for the C# branch: C# Next Generation. LOL

  • Anonymous
    June 23, 2009
    Joku,

  1. "best of both" is alsmost always an impossible goal in any area. If something can be used for "A" and for "B", then it is alomst inevitable that adding the support for "B" has imposed some constraints on the implementation of "A" (along with the reverse).
  2.  "the cost of adding new stuff" always exists. Hopefully the positive effect significantly outweighs the negative effect. Consider a situation where I hire four coders who each know 95% of version "n". statistically, up to 15% of the code base that gets developed can not be effectively maintained by randomly assigning it to a programmer.   If the language capabilities expand by 10%, then it is likely that the average percentage of knowledge/experience will drop. Initially this will be because few people know the new stuff. This is not so bad of an impact because existing codebases will tend to consist of the older established capabilities. But, over time the difference between old and new will fade. Unfortunately history has shown that the amount of work people invest before considering themselves "competent" remains about the same. This results in the average candidte knowing a smaller subset, and the subsets between different candidates diverging. At this point, putting together a team of four is statistically going to have the probability that over 35% of the codebase may contains some feature that a given team member is not familiar with. disclaimer: the numbers used are mathematically valid, but make "worst case" assumptions; they also get worse as the size of a team increases. In my 25+ years working as a consultant involved with various teams of all levels, I have found this principal to hold true. There are some management steps which can be taken to mitigate this effect, but they typically run into one of two problems. Some constraints can be very difficult to enforce in an automated way. Other times certain "excluded" items present a compelling case (for the team member who are knowledgable about the item).
  • Anonymous
    June 24, 2009
    I just want to say that I think adding global methods would be a horrible idea. Please don't put them in there. I don't want to go back to the old days of C where programmers putting everything in global variables and the code is impossible to read. Don't go making it like JavaScript either. I want strongly typed objects.

  • Anonymous
    June 24, 2009
    The comment has been removed

  • Anonymous
    June 24, 2009
    Another vote for keeping it simple.  I do love lambda expressions, so there's no way I'd say C# 2.0 was the best, but please don't tack on everything.  If you add every feature from every other .NET language, then what exactly is the point of all the interoperability?

  • Anonymous
    June 24, 2009
    The comment has been removed

  • Anonymous
    June 24, 2009
    +1 for doing.. It is the one thing that I would most like to be more compatible with Python and F#!

  • Anonymous
    June 24, 2009
    The comment has been removed

  • Anonymous
    June 24, 2009
    Revisiting this topic and the comments after your new post, I'm particularly struck by JM's comment which just precedes this one. I'd rather forgotten that it was possible to have a multi-language assembly, although at one point I was watching that development with considerable interest. In my opinion, if you really want to take a script-like approach for some items of functionality and a more obviously strongly-typed approach for others, then supporting different languages within a single Visual Studio project would be the way to do it. Requiring the use of a different language, in a different source file, with a different icon, etc., is a type of encapsulation that would make it easier to delineate the boundaries between one approach and the other. Better doing that than blurring the lines within a language. I take your point about considering all the possibilities, and recognise it's a good thing; but of course you're aware of the danger of diluting the things admire about C# for niche benefits. I hope the feedback you get from positing these things online is helpful; from outside Microsoft, the chance to comment is greatly appreciated. I also hope the feeedback here reflects the bigger picture, since it agrees with my opinions ;-)

  • Anonymous
    June 25, 2009
    I was going to give a +1 for multilanguage assemblies, but think through what would be required for that in terms of dependency resolution.  Assume you have a class F written in F#, with dependencies on a class C written in C#, in turn with dependencies on class F.  With the language compilers being separate executables (hence each class would need to be compiled, incompletely, by a different program), think of what it would take to compile this.  If it takes compiler changes, now realize how limited your language interop scenario becomes.

  • Anonymous
    June 25, 2009
    +1 for multi-language assemblies. THIS is where the future of interoperability lies. @Mark, The main scenarios for multi-language assemblies would not require circular dependencies, so leaving them out and calling it a limitation would not significantly reduce the usability of the feature.

  • Anonymous
    June 25, 2009
    Regarding multi-language assemblies - getting basic VS support for this would be a good start. I had a Connect ticket opened for that: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=387155 Note that this doesn't talk about adding full-fledged VS support. You'd still have to muck with .proj files directly in this scenario (though once done, VS build would happily use that). However, for now, its resolution is rather disheartening - apparently, "the customer impact of doing this is fairly small" - which may well be the case, since I'm just one guy asking for a feature, and have no means of knowing how many more would find it useful. So - if you want it, go ahead and vote for it, send your usage scenarios, and so on.

  • Anonymous
    June 28, 2009
    The comment has been removed

  • Anonymous
    June 29, 2009
    The comment has been removed

  • Anonymous
    June 30, 2009
    @Denis, The decision between safety and power is not one or the other. Rather there is a continuum between them, the more power you have, the less safe you are (for the most part), and vice-versa, The trick of language design is to determine what level of safety you are willing to give up for what level of power. That is what the blog post and the comment debate is about, and I see nothing wrong with it. Nor do I think that the level of debate has come anywhere close to the point of invoking Godwin's Law. Nor do I see what Rudyard Kipling of Jack London have to do with any of this.

  • Anonymous
    July 11, 2009
    @commongenius, It's not the more power you HAVE the less safe you are; its' more like, the more power you USE the less safe you are. That is, in essense, my point: if someone is not comfortable with a certain level of power, he is free NOT TO USE it; it does not mean that he SHOULD NEVER HAVE AN OPTION to use it, should he change his mind. I'm sure your car has something like 160 mph or 260 kph on its speedo; have you really gone that fast on a public road? Or should we now declare any car that POTENTIALLY CAN reach that speed dangerous and ban them all outright? It's the same with a language: it does not FORCE you to use all its powers, and there are always "some others" who may find the powers we avoid (for the safety's sake) extremely useful. So who is any one of us to decide for EVERYONE ELSE what's right and what's wrong for them? The more powerful the language is, the more of us are able to decide it FOR OURSELVES, and I see nothing wrong with that. As for Kipling and London, they are just examples of how the fear of controversy, manifesting itself in the so-called "political correctness", criples the culture: just because they occasionnally used some words, and rised some subjects, that someone, somewhere might find offensive, their works are disappearing from people's memory. I would hate to see C#, or any other good and powerful language, cripled out of fear of its power.

  • Anonymous
    July 12, 2009
    Denis, It seems YOU (and not commongenius) are the one missing the point. When dealing with a business development environment, is can be impossible to ENFORCE the useage of only "authorized" constructs. Code reviews do NOT ENSURE that they will not be used, only automated tools can hope accomplish this [and I have previously posted about the lengths some programmers will go to circumvent these, eve in the face of being fired]. Once it is POSSIBLE to use something, you have to assume that it may be used. Then you have to start figuring out how to make sure it is used corredtly. I am NOT a ludite, and actually vote in favor of many of the features. I KNOW that the team at Microsoft (especially Eric) understands this COST. You statement  of "The more powerful the language is, the more of us are able to decide..." is the exact opposite of what heas been thre repeated experience of nearly every long term developer. The ONLY people who are truely qualified to make a decision (on any subject) are those who fully understand 100% of the alternativies and the risk/rewards of each. And the larger the required knowledge base is the smaller the percentage of people that will have the full knowledge. Your statement

  • Anonymous
    July 12, 2009
    @TheCPUWizard, As you yourself say, "the ONLY people who are truly qualified to make a decision (on any subject) are those who fully understand 100% of the alternatives and the risk/rewards of each." Now, the question is, WHO are those people? ARE there any such people, ever??? As I move to software architecture and project management after slaving away as a developer workhorse for 19 years, I know how tempting it is to Command And Control: to Show Them All Who's The Real Boss, to finally get your vindication. I also remember how much resentmen such methods inspire: THAT is the true reason behind the ridiculous "lengths some programmers will go to circumvent these [automated tools enforcing the coding standards], even in the face of being fired". So I say: yes, work with your team on the GUIDELINES for best coding practices, but understand that life CANNOT be totally formalised and there is ALWAYS a situation in which the law is meant to be broken. Like in a game of chess: as soon as you program your chess-playing application NEVER to place a knight in the corner, for example, it immediately runs into a situation in which such a move is the only chance to win (or not to loose). Also, more trust in your people will not hurt: they will not push the boundaries more than necessary, UNLESS YOU LIMIT THEM more that necessary. Think Scrum: empowered, self-organizing team. I've just got my CSM certification, so I still remember... And once again: NO ONE understands 100% of the ramification of each alternative, so it just might be convenient to keep them all handy, just in case the law we lay down proves to be wrong, and there is an urgent need for an alternative.