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.

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

  • I'm not sure this answers the question and/or many potential use cases. Scripty programming is all very nice – but frankly, I don't need C# for that. No, the top objection to this restriction that I have is that there's often no logical connection between (static helper) classes and their static methods. They simply serve as organization units – but this job is usually done by namespaces in .NET.

    Your point is essentially that C# forces the organization of code into components, but not all code logically fits into a particular component. I agree that this is the case. This is always the case in programming languages; not all possible code fits into the "object" paradigm, the "component" paradigm, the "function" paradigm, the "imperative" paradigm, the "declarative" paradigm, and so on. The question at hand for the language designers is: given that we believe there is value in component-oriented programming, how many tools do we want to give the user that allow them to work against those design goals when they are in one of those situations that meshes poorly with that paradigm? That's a judgement call; the benefit has to be pretty high in order to be worth bending one of the core principles of the language design. -- Eric 

    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 (albeit using very different ways, the first by importing members of a class, the second one via modules).

    Finally, extension methods have *partially* mitigated this problem – but still, in many cases free functions are syntactically superior to methods.

  • I have to vote strongly againt this idea. Within an Object Oriented environment, everything should be bound to a Type or an Instance of said type.

    If you allow for "free" functions, then to be consistant you need free variables (and properties). This makes Pandora's box seem like a "good" idea.

    [Eric, I know you have heard this from me before...]

    Attempting to make C# (or any language) be "one size fits all" is doomed to failure and in the process will make the language harder to use for the exact purposes it was created.

    The "extension" point should be the .NET environment; which is exactly what it was created for. If a project can benefit from using multiple design techniques; then use multiple programming languages.

    Even if the budget and resources were unlimited [reducing the development cost to 0 for any possible feature], this (extending capabilities by multiple languages) would still be my preferred way to go....

  • I see this as a feature that lends itself to abuse.  Now, I know it's just a tool, and as with any tool the use or mis-use of it depends on who's holding it.  But I think this would be a tool that _encourages_ abuse.  It's not something I would want to see in the language proper.

    What I _would_ like is a "C# for Scripters" product.  And it should be a completely separate product, or maybe just a different project type in visual studio.  It would include better access to the WMI libraries (System.Management imported by default, and more strongly-typed classes added to it), support to make it easy to automate other programs (easily click a button or modify a textbox based on active window, process, or screen coordinates, and getting said process handles, windows, etc), and it would be something you can just start up (and it should start _very_ quickly) and start entering code.  This would also be useful as a scratchpad environment for "normal" programming.

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

  • TheCPUWizard said: "I have to vote strongly againt this idea. Within an Object Oriented environment, everything should be bound to a Type or an Instance of said type."

    Try thinking about it this way: a static method inside a class is really just a global method with a dot in its name.

    If that makes it 'Object Oriented', then apparently 'Object Oriented' means very little!

    Furthermore, it is frequently a minor implementation decision between writing a simple helper class or writing a method, especially with the existence of delegates, anon-methods/lambdas and iterators. An iterator method is in some ways a class declaration - it defines an object type implementing a state machine. So the line between classes and methods is somewhat blurred.

    If Enumerable.Range was written as a class:

    public class Range : IEnumerable

    Then it would be allowed to go in a namespace. But as it's been implemented as a method, it's not. So it has to have a name with a dot in it.

    And namespaces clearly overlap a great deal conceptually with static classes; one difference is that the same namespace can be contributed by code in different assemblies, but nevertheless it is odd that both exist if you ignore the historical evolution of the language and platform (which of course can't be ignored in reality).

  • It is less about being object-oriented and more about being organized, especially in environments with multiple developers. If people can throw functions into the root namespace (say, CompanyName.) people will, and that will be detrimental to placing methods into the proper components where they make sense  (even for the cases where it is not always clear where a method belongs and/or might make sense in multiple classes).  

  • To all people complaining that global methods are not object-oriented enough, or pollute namespaces.

    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.

    Now, stop and think about it for a moment. Do you seriously claim that, by prefixing every static method with "Utility.", you 1) actually reduce scope pollution in any meaningful way, and; 2) make the code object-oriented?

    OOP is about classes _and objects_. "Static classes" are a weird artifact of the simplification that many languages - including C# - make by only allowing classes at top level. They have absolutely no correspondence in object-oriented design theory; ultimately, they're just a hack. Granted, it's the one that simplifies language implementations and tools, and (perhaps) makes the language itself slightly more regular and thus easier to learn, so we go along with that.

    Now as for scope pollution - it is precisely what namespaces are for. No-one is talking about shoving methods into global namespace (besides, you can do it with types alread anyway). The idea is that e.g. System.Math should ideally be a namespace, not a type - so that you can import it, and directly use the functions within. Obviously, there's no scope pollution involved - the functions are still confined to their specialized namespace.

  • Lets use pmnieav's example of System.Math.  It not only contains Methods it also contains (const) Fields. This directly supports my previous position on the effects of consistantly supporting "naked" functions.

    Even more importantly, the class has two PRIVATE items [maxRoundingDigita and roundPower10Couple], to tansform this functionallity you would need to further complicate the structure of the language.

    I ask every one to remember OCcam's razor, and also Scott Meyer's time tested advice - "Don't MolleCoddle your users!!!" - give them ONE way to accomplish a task - choices with no cllear distinction as to which is appropriate for a given situation COSTS MONEY in every program that is developed using that tool (language). In other words, every time a feature is added to the language [any language] it incrementally increases the cost of developing ANY program using that language.

    I tend to agree with Jason that (from a language perspective) C# 2.0 really did hit the "sweet spot"; the BCL additions that make up 3.0 and 3.5 are extremely valuable, but are not language issues per se...

  • @pminaev,

    The fact that most developers don't bother to think about the appropriate structure of their program and tend to just shove everything into a god class (the VB application I am currently maintaining calls it modMain) is NOT a valid argument for allowing top-level methods. It just means that most developers are lazy, and I am not in favor of taking laziness into account when designing a professional language.

    I also strongly agree with TheCPUWizard. A huge amount of effort has gone into making it easy to interoperate between multiple languages on the .NET platform, which means it is trivial (in most cases) to use the right language for your situation while still taking advantage of all of the great features and libraries of .NET. Yet we still hear developers clamoring for <their favorite language> to get <feature X> so that it will be easier for them to implement <something for which the language was not designed> without having to learn anything new. The inevitable result of this effort to toss everything into the same bucket is a bloated language which topples under its own weight; all you have to do to see this effect is look at C++.

    In short, lets PLEASE keep C# component-oriented, and leave other competing design goals to other languages. I really don't want to have to abandon C# in five years because it has become to heavy; we are already way too close to that point.

  • I would just like the f# repl to become much stronger and easier to use...

    Making it easier for all referenced projects to be automatically loaded for example, or to cancel a running command without having to kill the session (and all associated state)

  • Yuck! My reasons have all been given by others in previous posts.

  • Visual Basic supports this in two different ways: modules and global imports.

    VB Modules are static classes which can have their functions called without specifying the module name. I don't like this behaviour because it puts the methods in two places at once (F and M.F).

    Global imports I do like. They are properties you set at the solution level, and act as an implicit imports (C# using) line at the top of each source file. This way VB source files don't all start with the same half dozen import statements.

    VB also has another feature related to this which C# doesn't have. If you've imported the System namespace, you don't need to include it when referring to classes in sub-namespaces (eg. Numerics.BigInteger instead of System.Numerics.BigInteger).

  • One perceived problem with having such top-level functions is that they will proliferate and it will become very hard (at least without relying heavily on the IDE) to tell where any given name is resolved to. However, that is already a problem with types. I find it a shame that in C# it's easy to import the entire contents of a namespace but hard to import individual names from that namespace. (You can do "using TextWriter=System.IO.TextWriter;", but it is a bit clunky and generally refactoring tools won't help you to write code like this.) It is the default "import everything from the namespace" using directive that makes it hard to tell where all the names resolve to.

    Personally I quite like Python's "from package import name1, name2, name3". This manages to be relatively succinct while remaining quite unambiguous about exactly which names from the package we're using. Conversely, it lets you know when you see those names in the file which package they came from.

    I wonder if top-level functions would be more palatable to some if the convention had historically been to use explicit using aliases and to avoid using entire namespaces. I guess we'll never know - there doesn't seem much can be done at this point. Even though I'd like a nicer syntax for explicitly using individual types *without* renaming them, any such change has so little benefit over standard "using aliases" that it could never claw its way into positive points.

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

Page 1 of 4 (58 items) 1234