dotnet / docs

This repository contains .NET Documentation.
https://learn.microsoft.com/dotnet
Creative Commons Attribution 4.0 International
4.12k stars 5.8k forks source link

Should the console project template use top-level statements #27420

Open tdykstra opened 2 years ago

tdykstra commented 2 years ago

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 commented 2 years ago

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

tdykstra commented 2 years ago

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

tdykstra commented 2 years ago

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.

q00Dree commented 2 years ago

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

tdykstra commented 2 years ago

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?

Rick-Anderson commented 2 years ago

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.

bravecobra commented 2 years ago

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.

Rick-Anderson commented 2 years ago

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

DenisBabarykin commented 2 years ago

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.

Rick-Anderson commented 2 years ago

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.

DenisBabarykin commented 2 years ago

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.

luetm commented 2 years ago

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.

symbiotic commented 2 years ago

@tdykstra thanks for starting this issue

bravecobra commented 2 years ago

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.

died commented 2 years ago

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

DefinitelyADev commented 2 years ago

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.

ahwm commented 2 years ago

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.

ghost commented 2 years ago

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.

svick commented 2 years ago

@syntechtix Top-level statements support await.

dgxhubbard commented 2 years ago

Bring old template back to net 6!

AlexMcCown commented 2 years ago

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.

cho7052002 commented 2 years ago

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

AlexMcCown commented 2 years ago

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.

brogdogg commented 2 years ago

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

Zzzzz

DefinitelyADev commented 2 years ago

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.

olivier-spinelli commented 2 years ago

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

AptiviCEO commented 2 years ago

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.

chrislarabell commented 2 years ago

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.

ahwm commented 2 years ago

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.

chrislarabell commented 2 years ago

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[]);
TylerCode commented 2 years ago

I will add my -1 to this feature. I don't really know who it helps. If I'm building a larger application, I want more control. If I'm building a tiny application, I probably need more libraries and if it's annoying to ADD more libraries then it's not doing me any favors.

nrjohnstone-omnia commented 2 years ago

Definitely adding a -1 to this feature. If you don't understand behind the scenes how a console application bootstraps itself, then your not helping new developers at all. Should not be part of the template or it should be an optional template, not the default.

nicasi commented 2 years ago

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

See 11th example here: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/methods. This is not possible in the new template unless I'm missing something.

static void Main(string[] args)
{
    int[,] matrix = new int[2, 2];
    FillMatrix(matrix);
    // matrix is now full of -1
}

public static void FillMatrix(int[,] matrix)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
    {
        for (int j = 0; j < matrix.GetLength(1); j++)
        {
            matrix[i, j] = -1;
        }
    }
}

I also teach C# to beginners and I don't like the new template very much; although I guess it could have it merits in programming 101 (in our school we use Python for programming 101).

KamilahBakery commented 2 years ago

top-level statements is a very very bad idea.......

enkeyz commented 2 years ago

Wouldn't be better if users could choose which templates they want, when creating a new project?

guoqiang5277 commented 2 years ago

top-level statements is a very very bad idea.......

brettman commented 2 years ago

I'd like to understand more of the rationale behind introducing such a confusing change. Started teaching myself programming with the beta 2 release of dotnet (~2001?) and one thing that never confused me was the structure of the Program.cs class. Writing hello world in a single line might sound attractive, but really, is Console.WriteLine really the point of hello world? I thought the point of HW was to introduce a developer to the syntax of the language. Now we have a situation where there is a totally different syntax for program.cs / main than for every other class. Feel like that's going to be more confusing and difficult to explain in the long run. Maybe there is some other benefit I don't see here, but I'm starting to doubt. Didn't they have any real problems to solve over in Redmond?

mas-co commented 2 years ago

Really? If this is to be the default template, it should compile out of the box---and it doesn't. "CS0103: The name 'Console' does not exist in the current context." This happens on all of my workstations. Not impressed.

ahwm commented 2 years ago

Really? If this is to be the default template, it should compile out of the box---and it doesn't. "CS0103: The name 'Console' does not exist in the current context." This happens on all of my workstations. Not impressed.

@mas-co it compiles for me out of the box. It's just bad design. I wonder if it's somehow creating using the .NET 6 template but building with a different version?

image

image

mas-co commented 2 years ago

I'm using VS2022. New project -> Console App -> project name, etc. -> Framework .NET 6.0 -> Create. As soon as the project opens and Program.cs is put in the editor, there is an error waiting in the error list. Double checked the target framework and it is, indeed, set to .NET 6.0

I never thought to use VS Code for c# when the IDE is available. (I really only use VS Code for gfortran stuff.) But VS2022 preview "broke" my laptop. Ever since then I cannot install VS2022 or VS2019 cleanly. But that's another story.... :(

AptiviCEO commented 2 years ago

@mas-co What was the error message?

For me, it works fine. It just gave me this terrible template and I have to mod it back to the old by placing the old structure to the main entry point code file.

Either way, it compiles fine.

mas-co commented 2 years ago

@EoflaOE "CS0103: The name 'Console' does not exist in the current context."

I can change it, to the old template, but why? My whole point is that it should compile as-is.

AptiviCEO commented 2 years ago

@mas-co Alright. I got your point. Would it help if you just put using System; statement on the top of the code?

mas-co commented 2 years ago

@EoflaOE Respectfully, I do not think you get my point.

Here is the whole template, and it matches, verbatim, the template from the URL in the template:

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

(There is no "using.System;" in the listing. ) They then go on, at length, about how they are using "implicit using directives" for

    using System;
    using System.IO;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net.Http;
    using System.Threading;
    using System.Threading.Tasks;

My point is that I have yet to see this work on an VS setup. Until they do, they should not change the default template to the broken one.

AptiviCEO commented 2 years ago

@mas-co I respect you. They shouldn't change what's not broken. Remember: If it ain't broke, don't fix it.

Then, you've discovered a bug if it can't find Console.

mas-co commented 2 years ago

@EoflaOE You just quoted one of my life's adages. :)

And I have sent this issue to MS via the Feedback. Hopefully they sort it out for me or they fix it.

mwwhited commented 2 years ago

This is a bad change. Fortually it has been partially "fixed" by the community. https://marketplace.visualstudio.com/items?itemName=Doomdied.ClassicConsole1

swmiller256 commented 2 years ago

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.

I have enough to worry about in building applications and debugging legacy code than to have to content with compiler magic. Since the compiler magic is not consistent across all source code types this adds no value.

tpressley commented 2 years ago

This change is incredibly frustrating. It wouldn't be so bad if I could create new methods in program.cs without having to completely recreate the entire file in the old format. The fact that I have to go and copy-paste the boiler plate from the old code just to add one method to a file is ridiculous, how could such an obvious regression in usability have made it into a production release?

glisean commented 2 years ago

This confused the crap out of me.