docs icon indicating copy to clipboard operation
docs copied to clipboard

Should the console project template use top-level statements

Open tdykstra opened this issue 2 years ago • 801 comments

For .NET 6, the console project template was changed to use top-level statements. The code that is created in Program.cs is:

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

In earlier versions of .NET and .NET Core, the code that is created in Program.cs is:

using System;
using System.Collections.Generic;
using System.Linq;

namespace MyApp // Note: actual namespace depends on the project name.
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

Discussion about whether this was a good change began in https://github.com/dotnet/docs/issues/26313 but there was some confusion about what exactly the various up or down votes applied to. This issue provides comments to vote on that clearly describe the options.

tdykstra avatar Dec 06 '21 17:12 tdykstra

Up-vote this comment if you support the use of top-level statements in project templates.

tdykstra avatar Dec 06 '21 17:12 tdykstra

Up-vote this comment if you prefer project templates to use the code created in previous .NET versions.

tdykstra avatar Dec 06 '21 17:12 tdykstra

Up-vote this comment if you would like to be given a choice between top-level statements code and the code created in previous .NET versions.

tdykstra avatar Dec 06 '21 17:12 tdykstra

I'm sorry in advance, but what about ASP.NET Core templates like -webapi ?
I'd like to have choice between normal Startup.cs and Program.cs vs new Program.cs

q00Dree avatar Dec 07 '21 05:12 q00Dree

what about ASP.NET Core templates like -webapi ? I'd like to have choice between normal Startup.cs and Program.cs vs new Program.cs

@Rick-Anderson do you want to create a discussion issue like this one in the ASP.NET Core repo?

tdykstra avatar Dec 07 '21 16:12 tdykstra

I'm sorry in advance, but what about ASP.NET Core templates like -webapi ? I'd like to have choice between normal Startup.cs and Program.cs vs new Program.cs

Use the ASP.NET Core 5 templates. Startup is fully supported.

Rick-Anderson avatar Dec 07 '21 20:12 Rick-Anderson

Use the ASP.NET Core 5 templates. Startup is fully supported.

That is only temporary as those templates will be removed once .net core 5 is out of support. So, no, that is not a valid permanent solution to the problem.

bravecobra avatar Dec 07 '21 20:12 bravecobra

The templates are not removed when .NET Core 5 goes out of support.

Rick-Anderson avatar Dec 07 '21 21:12 Rick-Anderson

The templates are not removed when .NET Core 5 goes out of support.

@Rick-Anderson can I create .NET Framework 4.5.1 template in VS 2022? Will I be able to create .NET Core 5 template in VS 2030? I don't think so. We want to make sure that the old-style explicit templates will be available in the future as well as the new one.

DenisBabarykin avatar Dec 07 '21 21:12 DenisBabarykin

https://dotnet.microsoft.com/download/dotnet/1.1 is the .NET 1.1 SDK. Download that and you can create the ASP.NET Core 1.1 templates with VS 2022.

If you have complicated startup logic, you might want to keep using startup. But with complicated startup logic, the templates aren't much help.

Rick-Anderson avatar Dec 07 '21 21:12 Rick-Anderson

But with complicated startup logic, the templates aren't much help.

@Rick-Anderson If you think that you are right, then people will also support your opinion. Organize a vote and the question will be closed.

DenisBabarykin avatar Dec 07 '21 21:12 DenisBabarykin

In my opinion, the move to remove clutter is a very good one. I was also quite shocked when I first saw it, but when I teach coding to newcomers, it makes things so much easier. No more "just ignore all that stuff and focus on the things between the {}".

In general it's a very good direction follow, as long as there's no hidden functionality ("magic defaults"). With that I mean hidden behavior, for example implicit Console.Foo() statements that are executed if we use top-level statements, but aren't if we use the explicitly defined approach. If it's just things that get in the way, and add no value, remove it. What would be valuable would be a good way with tooling to 'escalate' to the cluttered view again if it's needed.

luetm avatar Dec 08 '21 09:12 luetm

@tdykstra thanks for starting this issue

symbiotic avatar Dec 08 '21 12:12 symbiotic

as long as there's no hidden functionality ("magic defaults")

But there is magic going on. The signature of the program's entry-point method is deduced from your code. The only way to control it, is by changing your code (using await and return statements) since it's magically generated on-the-fly by the compiler. The relevant magic can be found at https://github.com/dotnet/roslyn/blob/main/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs#L37

So, no, it doesn't get in the way and yes, it does add value. It makes the method signature explicit and it makes the syntax consistent with any other cs file. The idea of 'less is more' does not apply here, imho. What used to be consistent by default, no longer is, by making namespace, class and method implicit. So, indeed, there are no more implicit Console.Foo() lines, which is a good thing, but now there is new implicit code/magic.

Keeping cs file syntax consistent over all files and learning the concept of classes and namespaces (which they would have to learn anyway at some point) seems an easier thing to understand for newcomers than compilers generating those classes and namespaces on-the-fly. I can see their questions like "Why is the syntax of Program.cs different from all the other files?" Answer: because the compiler can do magic.

In short, I think that syntax consistency wins easily over lines of code.

bravecobra avatar Dec 08 '21 12:12 bravecobra

BTW, can we have a MVC version Identity ? After ASP.NET Core 2.1 Identity only provider as Razor pages and it was hard to customize, unless .NET team only doing "Hello World" level project, or only provide Razor version Identity was not smart move, like this "top-level statements" issue.

And .NET team only close issue to close our voice like #24181

died avatar Dec 09 '21 10:12 died

BTW, can we have a MVC version Identity ? After ASP.NET Core 2.1 Identity only provider as Razor pages and it was hard to customize, unless .NET team only doing "Hello World" level project, or only provide Razor version Identity was not smart move, like this "top-level statements" issue.

And .NET team only close issue to close our voice like #24181

Since that happened I/we tend to just do it manualy. Eg. create our own views and models and add the service injections. I/We also have abstracted the (User/Role/etc)-Manager using our own interfaces and implementations. So we just add my/our own library in my/our projects.

DefinitelyADev avatar Dec 09 '21 15:12 DefinitelyADev

No more "just ignore all that stuff and focus on the things between the {}".

IMHO if this is the style of teaching then the teaching needs to be fixed. Namespaces and class declarations aren't to be ignored but understood. Coding classes should start of with conceptual lessons (such as "what are classes?") before ever writing code. Especially with a language like C# where everything is object oriented. Without that foundation the differences between Console.Write() and objA.Write() would be confusing ("Why doesn't Console require new?").

For new students it might be sufficient to explain that it's a class declaration and that they would be explored more fully later. The new templates just abstract everything away to background magic and, as others have mentioned, make it even more confusing to understand why Program.cs behaves differently.

Also see this comment https://github.com/dotnet/docs/issues/26313#issuecomment-973971021 for good examples.

ahwm avatar Dec 09 '21 20:12 ahwm

This is too much abstraction away from the core. What if I wanted to use the async version? I have to create a .net 5 project and alter it. This is going a bit too far.

ghost avatar Dec 10 '21 13:12 ghost

@syntechtix Top-level statements support await.

svick avatar Dec 10 '21 13:12 svick

Bring old template back to net 6!

dgxhubbard avatar Dec 14 '21 21:12 dgxhubbard

I see zero sense in abstracting main this heavily, please change it back and please don't let whoever made this decision make others.

The only possible use I can find for this is quick testing but that's what vscode is for.

AlexMcCown avatar Dec 16 '21 04:12 AlexMcCown

is synthetic sugar of hiding 12 lines of code worth these?

  • implicitly defined string[] args
  • implicitly defined using directives
  • fixed Main method signature
  • no backward compatibility of program enterance code
  • confuses programmers

cho7052002 avatar Dec 16 '21 04:12 cho7052002

In my opinion, the move to remove clutter is a very good one. I was also quite shocked when I first saw it, but when I teach coding to newcomers, it makes things so much easier. No more "just ignore all that stuff and focus on the things between the {}".

In general it's a very good direction follow, as long as there's no hidden functionality ("magic defaults"). With that I mean hidden behavior, for example implicit Console.Foo() statements that are executed if we use top-level statements, but aren't if we use the explicitly defined approach. If it's just things that get in the way, and add no value, remove it. What would be valuable would be a good way with tooling to 'escalate' to the cluttered view again if it's needed.

I'm sorry what are you ignoring in C#? Yeah namespaces are not super necessary all the time but you need main, you need your inclusions. What if you need to make main async? What libraries are in use? What libraries do you need? Where do you put out of main methods?

I honestly and truly worry about your teaching style if you jump right past includes and main and namespaces.

AlexMcCown avatar Dec 16 '21 04:12 AlexMcCown

Yeah, in not a huge fan of the new template. But I'm now over 40, so maybe old man complaining here. 🤔

Zzzzz

brogdogg avatar Dec 17 '21 02:12 brogdogg

Yeah, in not a huge fan of the new template. But I'm now over 40, so maybe old man complaining here. 🤔

Zzzzz

If it helps I'm 27.

DefinitelyADev avatar Dec 17 '21 07:12 DefinitelyADev

@brogdogg I'm 52 (taught C# and developing all day long) and I'm complaining a LOT!

olivier-spinelli avatar Dec 17 '21 11:12 olivier-spinelli

I personally don't like this change, because it creates more confusion. I'm reliant on the backwards compatibility.

It compiles flawlessly. However, I need the old design back.

AptiviCEO avatar Dec 17 '21 19:12 AptiviCEO

Keep it!

At first, I was surprised, too. Then I looked at the compiler code. Turns out, if you want to have a return value, you can add the return keyword and a value as the last statement. Or, if you just feel like writing return because you like the warm and fuzzy feeling you get, go for it, it works! If you want the program to run asynchronously, you can add the await keyword in front of an asynchronous statement. Also, if you need to write a using statement, you write using <libraryname>;.

The concept of this being magic is more along the lines of this being more magical than having to write all of the boilerplate code. The compiler is magic.

As far as teaching new coders how to code, this is valuable. Imagine writing "Hello World" in exactly one line of code! It also makes you think, "Do I really need to include System and its friends at the top of every class?"

The Main method should be the most boring method in your program. It is the entry point, not the "let's cram everything this program can do between these two brackets" point. Since OOP is about Os, why not create a class that encompasses the functionality of your P.

Also, I'm not punch cards and mainframes, but I'm also not new school. I think it is okay to have your cheese moved once in a while. It makes you a better programmer.

chrislarabell avatar Dec 17 '21 19:12 chrislarabell

Keep it!

...

Also, I'm not punch cards and mainframes, but I'm also not new school. I think it is okay to have your cheese moved once in a while. It makes you a better programmer.

The debate isn't so much about keeping the feature as it is about what the default template looks like. Sure, the feature has its place and few are truly debating that (being able to run an MSDN example without all the extra boilerplate is a big plus, for example). But outside of super basic examples, this feature generally hinders and confuses. Real world situations call for more fine-tuned control and, therefore, require reverting back to the non-top level statement template.

Hence the request for an option for those that want to use the new template. That way everyone will be happy. Those who want the old style can use it while those that don't won't be forced to use hackery and workarounds.

ahwm avatar Dec 17 '21 20:12 ahwm

Keep it! ... Also, I'm not punch cards and mainframes, but I'm also not new school. I think it is okay to have your cheese moved once in a while. It makes you a better programmer.

The debate isn't so much about keeping the feature as it is about what the default template looks like. Sure, the feature has its place and few are truly debating that (being able to run an MSDN example without all the extra boilerplate is a big plus, for example). But outside of super basic examples, this feature generally hinders and confuses. Real world situations call for more fine-tuned control and, therefore, require reverting back to the non-top level statement template.

Hence the request for an option for those that want to use the new template. That way everyone will be happy. Those who want the old style can use it while those that don't won't be forced to use hackery and workarounds.

I disagree. Why would they keep a feature that no one wants or uses? There would be no need for this thread if it were as simple as adding a checkbox.

I'm not sure what real-world situations would require you to type out the Main method, but I am curious.

In addition, it may be a hinderance or confusing for some, but as I said previously, the entry point is a place where the program starts. If you have a class that runs your application, say, for example TestClass with an async method, you can write this:

TestNewTemplate.TestClass tc = new();

await tc.TestAsyncFunctionality(args[]);

Or:

using TestNewTemplate;

TestClass tc = new();

int result = await tc.TestAsyncFunctionality(args[]);

return result;

Or make it static:

return await TestNewTemplate.TestClass.TestAsyncFunctionality(args[]);

chrislarabell avatar Dec 17 '21 21:12 chrislarabell