
C# 13 and .NET 9 – Modern Cross-Platform Development Fundamentals
By :

The goal of this section is to showcase how to build a console app using Visual Studio.
If you do not have a Windows computer or want to use VS Code, then you can skip this section, since the code will be the same; just the tooling experience is different. However, I recommend that you review this section because it does explain some of the code and how top-level programs work, and that information applies to all code editors.
This section is also available in the GitHub repository (so it can be updated after publishing if needed) at the following link:
https://github.com/markjprice/cs13net9/blob/main/docs/code-editors/vs.md
If you want to see similar instructions for using Rider, they are available in the GitHub repository at the following link:
https://github.com/markjprice/cs13net9/blob/main/docs/code-editors/rider.md
Let’s get started writing code:
console
in the Search for templates box, and then select Console App.Make sure that you have chosen the cross-platform project template, not the one for .NET Framework, which is Windows-only, and the C# project template rather than another language, such as Visual Basic or TypeScript.
HelloCS
for the project name, C:\cs13net9
for the location, and Chapter01
for the solution name.Screenshots of Visual Studio when creating new projects can be found in the GitHub repository at the following link: https://github.com/markjprice/cs13net9/blob/main/docs/ch01-project-options.md.
You can install as many .NET SDK versions as you like. If you are missing a .NET SDK version, then you can install it from the following link: https://dotnet.microsoft.com/en-us/download/dotnet.
Program.cs
to open it, and note that Solution Explorer shows the HelloCS project, as shown in Figure 1.4:Figure 1.4: Editing Program.cs in Visual Studio
Program.cs
, note that the code consists of only a comment and a single statement, as shown in the following code:
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
This template uses the top-level program feature introduced in C# 9, which I will explain later in this chapter. As the comment in the code says, you can read more about this template at the following link: https://aka.ms/new-console-template.
Program.cs
, modify line 2 so that the text that is being written to the console says Hello, C#!
.All code examples and commands that you must review or type are shown in plain text, so you will never have to read code or commands from a screenshot, like in Figure 1.4, which might be too small or too faint in print.
The next task is to compile and run the code:
Good Practice: When you start a project in Visual Studio, you can choose whether to attach a debugger or not. If you do not need to debug, then it is better not to attach one because attaching a debugger requires more resources and slows everything down. Attaching a debugger also limits you to only starting one project. If you want to run more than one project, each with a debugger attached, then you must start multiple instances of Visual Studio. In the toolbar, click the green outline triangle button (to the right of HelloCS in the top bar shown in Figure 1.5) to start without debugging, instead of the green solid triangle button (to the left of HelloCS in the top bar shown in Figure 1.5), unless you need to debug.
Figure 1.5: Running the console app on Windows
HelloCS.csproj
project file shows that this project has its target framework set to net9.0
, as shown in Figure 1.6.bin
and obj
folders are visible, as shown in Figure 1.6:Figure 1.6: Showing the compiler-generated folders and files
Two compiler-generated folders were created, named obj
and bin
, as described in the following list:
obj
folder contains one compiled object file for each source code file. These objects haven’t been linked together into a final executable yet.bin
folder contains the binary executable for the application or class library. We will look at this in more detail in Chapter 7, Packaging and Distributing .NET Types.You do not need to look inside these folders or understand their files yet (but feel free to browse around if you are curious).
Just be aware that the compiler needs to create temporary folders and files to do its work. You could delete these folders and their files, and they will be automatically recreated the next time you “build” or run the project. Developers often delete these temporary folders and files to “clean” a project. Visual Studio even has a command on the Build menu named Clean Solution that deletes some of these temporary files for you. The equivalent command with the CLI is dotnet clean
.
If you have seen older .NET projects before, then you might have expected more code, even just to output a simple message. This project has minimal statements because some of the required code is written for you by the compiler when you target .NET 6 or later.
If you had created the project with .NET SDK 5 or earlier, or if you had selected the checkbox labeled Do not use top-level statements, then the Program.cs
file would have more statements, as shown in the following code:
using System;
namespace HelloCS
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}
}
During compilation with .NET SDK 6 or later, all the boilerplate code to define the Program
class and its Main
method is generated and wrapped around the statements you write.
This uses a feature introduced in .NET 5 called top-level programs, but it was not until .NET 6 that Microsoft updated the project template for console apps to use top-level statements by default. Then, in .NET 7 and later, Microsoft added options to use the older style if you prefer:
dotnet
CLI at the command prompt, add a switch:
dotnet new console --use-program-main
Warning! One functional difference is that the auto-generated code does not define a namespace, so the Program
class is implicitly defined in an empty namespace with no name, instead of a namespace that matches the name of the project.
Key points to remember about top-level programs include the following:
using
statements must be at the top of the file.Main
if you explicitly define it, the method is named <Main>$
when created by the compiler.The using System;
statement at the top of the file imports the System
namespace. This enables the Console.WriteLine
statement to work. But why do we not have to import it in our project?
The trick is that we still need to import the System
namespace, but it is now done for us using a combination of features introduced in C# 10 and .NET 6. Let’s see how:
obj
, Debug
, and net9.0
folders, and open the file named HelloCS.GlobalUsings.g.cs
.System
for use in all code files, as shown in the following code:
// <autogenerated />
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;
bin
and obj
folders.I will explain more about the implicit imports feature in the next chapter. For now, just note that a significant change that happened between .NET 5 and .NET 6 is that many of the project templates, like the one for console apps, use new SDK and language features to hide what is really happening.
Now let’s discover how the hidden code has been written:
Program.cs
, after the statement that outputs the message, add a statement to throw a new exception, as shown in the following code:
throw new Exception();
Program
class was defined by the compiler, with a method named <Main>$
that has a parameter named args
to pass in arguments, as shown in Figure 1.7 and the following output:
Hello, C#!
Unhandled exception. System.Exception: Exception of type 'System.Exception' was thrown.
at Program.<Main>$(String[] args) in C:\cs13net9\Chapter01\HelloCS\Program.cs:line 3
Figure 1.7: Throwing an exception to reveal the hidden Program.<Main>$ method
Now, let’s discover what namespace the Program
class has been defined within:
Program.cs
, before the statement that throws an exception, add statements to get the name of the namespace of the Program
class, and then write it to the console, as shown in the following code:
string name = typeof(Program).Namespace ?? "<null>";
Console.WriteLine($"Namespace: {name}");
??
is the null-coalescing operator. The first statement means, “If the namespace of Program
is null
, then return <null>
; otherwise, return the name.” You will see more explanations of these keywords and operators throughout the book. For now, just enter the code and run it to see what it does.
Good Practice: Code editors have a feature named code snippets. These allow you to insert pieces of code that you commonly use, by typing a shortcut and pressing Tab twice. For example, in Visual Studio, to enter Console.WriteLine()
and leave the cursor in the middle of the parentheses ready for you to type what you want to output, type cw
, and then press Tab, Tab. Read the documentation for your code editor to learn how to insert code snippets using shortcuts.
Program
class was defined without a namespace, as shown in the following output:
Namespace: <null>
Let’s add a second project to our solution to explore how to work with multiple projects:
Warning! The above step adds a new project to the existing solution. Do NOT navigate to File | New | Project…, which instead is meant to be used to create a new project and solution (although the dialog box has a dropdown to choose to add to an existing solution too).
AboutMyEnvironment
, leave the location as C:\cs13net9\Chapter01
, and then click Next.Warning! Make sure you have selected the Do not use top-level statements checkbox so that we get to see the older style of Program.cs
.
AboutMyEnvironment
project, in Program.cs
, note the statements to define a namespace that matches the project name, an internal class named Program
, and a static method named Main
with a parameter named args
that returns nothing (void
), as shown in the following code:
namespace AboutMyEnvironment
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}
}
Program.cs
, in the Main
method, replace the existing Console.WriteLine
statement with statements to output the current directory, the version of the operating system, and the namespace of the Program
class, as shown in the following code:
Console.WriteLine(Environment.CurrentDirectory);
Console.WriteLine(Environment.OSVersion.VersionString);
Console.WriteLine("Namespace: {0}",
typeof(Program).Namespace ?? "<null>");
AboutMyEnvironment
project (or any file or folder within it), and note that Visual Studio indicates that AboutMyEnvironment
is now the startup project by making the project name bold.Good Practice: I recommend this way of setting the startup project because it then makes it very easy to switch startup projects by simply clicking a project (or any file in a project) to make it the startup project. Although you can right-click a project and set it as a startup project, if you then want to run a different project, you must manually change it again. Simply clicking anywhere in the project is easier. In most chapters, you will only need to run one project at a time. In Chapter 15, Building and Consuming Web Services, I will show you how to configure multiple startup projects.
AboutMyEnvironment
project, and note the result, as shown in the following output and Figure 1.8:
C:\cs13net9\Chapter01\AboutMyEnvironment\bin\Debug\net9.0
Microsoft Windows NT 10.0.26100.0
Namespace: AboutMyEnvironment
Figure 1.8: Running a console app in a Visual Studio solution with two projects
Windows 11 is just branding. Its official name is Windows NT, and its major version number is still 10! But its patch version is 22000 or higher.
When Visual Studio runs a console app, it executes it from the <projectname>\bin\Debug\net9.0
folder. It will be important to remember this when we work with the filesystem in later chapters. When using VS Code, or more accurately, the dotnet
CLI, it has different behavior, as you are about to see.
Change the font size
Change margin width
Change background colour