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

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

Rate This
  • Comments 58

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.

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

  • "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.

    No doubt Eric is already aware of this, and of course it's not really relevant to the question anyway (wisely, not one of the six criteria involved in the question of whether to do a feature or not has anything to do with what other languages do)

    As far as this goes:

    "Open a big project that you work on. Search for files named "Utility.cs" (or "StaticMethods.cs", or "Helpers.cs") in the tree. Count the number of files found."

    I only have classes like that when it's a small, "rough-cut" program and just a handful of these kinds of "no-man's land" methods.  Otherwise, these kinds of methods do wind up in specific static classes for the purpose of organization.

    Could one accomplish something similar by simply putting them into a namespace, and not requiring that they group into a class?  Sure, I suppose so.  But why?  In what way does that significantly benefit my code?  It doesn't.  It's trivial to type the correct class name to qualify the method, and doing so actually makes the code a bit more clear.  If I'm skimming through the code, it's nice to have immediate indication (for example) that I'm calling the Truncate() that will return the integer portion of a number rather than the Truncate() that resets the file length to 0.

    Sure, with additional analysis one could probably still figure out the difference in such cases without too much trouble, but a) every exception to that generality argues against top-level methods, and b) for these kinds of static methods, the code is still easier to understand when it's more explicit, even as it's possible to decipher it even if the class/namespace name isn't explicit.

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

  • >>"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.

  • "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.

  • "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.  :)

  • +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.

  • 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?

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

  • I have scripting background before coming to C#. I'd really love the ability to be scripty (WITH good intellisense, unlike most scripty languages). I'd go as far as replace powershell with a C# command line while keeping the C# perf and adding some usual command line aliases to c# code. I just don't dig powershell syntax and I believe you could accomplish the powershell style of tasks with c# with just as little typing if there was suitable functionality in the framework and intellisense was heavily used.

    However I also agree with the concerns represented here. C# programs just look cleaner as there's some structure and organization there always.

    I think there has to be some kind of 'best of both' approach and maybe you should make a post suggesting some and also offer readers a chance to discuss and suggest their own.

    I believe that at somepoint, the cost of adding new stuff while keeping existing users happy will start to either impede progress or have some other negative effects. Even just perceived, not real, negative effects could hurt language in the long run and I believe that's something MS needs to consider.

    What I'd consider is to at some point branch the C# language such that the language reciding in .cs files gets only minor upgrades if absolutely necessary and the new developments would go into files with new file extension with strong IDE support such as ability to perfectly convert old code to whatever modifications were made that broke backward compat in the branch. That's also where I'd put these top level methods.

  • Oh and I have to suggest a name for the C# branch: C# Next Generation. LOL

  • 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).

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

  •     I agree with the "don't add this" crowd.  This feels to me like a technological solution to accomodate a combination of laziness (not creating logical places for the code) and style differences (those who don't like Class.Method), both of which are human problems.

    I like most of the reasoning against it, but I think that one important example has been overlooked, the cleanup of code to make it more readable for people that do not like to have StaticClassName.StaticMethodName everywhere, and that is to write a local wrapper method, or better yet, have Visual Studio do it for you.  In the below sample, I used Refactor, Extract Method on Math.Sin(s), which created a private static method, which can now be called within the current class.  You can remove the static from the local Sin method as well.

           private void GetSinOfZero()

           {

               double s = 0;

               // Original line :

               // s = Math.Sin(s);

               s = Sin(s);

               this.Text = s.ToString("0.0000");

           }

           private static double Sin(double s)

           {

               return Math.Sin(s);

           }

        I think that this is a "Best of Both" solution using the tools that are already in C#.  It keeps the calling code readable, and if you need to see the true method invocation, it is right there for you.  I believe that the compiler is smart enough to see that the wrapper is just that and directly wire up the code as if it were a direct call to Math.Sin, so performance should not be an issue.

        The downside to this is that you need to create new method wrappers for each class, but that is easy to do and easy to put into a central location where commonly used ones can just be copied and pasted.  You could probably even automate that by using partial classes and adding a T4 template that includes a common set of wrappers for each class that you want to have them, reading them from a central file, if you really feel the need to do that.

  • 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?

Page 3 of 4 (58 items) 1234