Table of Contents
When you’re first getting started in a new programming language or with a new development environment, you need to figure out how to install the toolchain, set up a project, and compile and run a program. This tutorial steps you through that process using C# and the .NET SDK using the classic “Hello, World!” program.1
Source code
The source code for this article is available at https://github.com/HowToCSharp/HelloWorld.
The C# code for “Hello, World”
Here is the C# code for “Hello, World”:
Console.WriteLine("Hello, World!");Yeah, that’s pretty much it. Put that in a module called Program.cs,
and you’re good to go. As you may know, C# is a class-based language,
and you may be confused as to how we can have a program without a class.
We’ll get back to that in a moment.
Before going there, let’s look at a variation of this program:
using System;
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}If you hop onto .NET Fiddle,2 you’ll see something similar to this by default in the code-editing window.
Let’s look at each element of this program:
using System;is a directive that tells the compiler to include theSystemnamespace in the program. TheSystemnamespace contains many of the basic types and functions that are used in C# programs. Recent versions of C# support something called “implicit usings,” which automatically include certain namespaces, including theSystemnamespace, and with this feature, you don’t need this line.public class Programdefines a class calledProgram. This is the main class of the program. Thepublickeyword means that the class is accessible from outside the assembly (i.e., the project). There’s nothing magic about the nameProgram; you could call it anything you like. The C# runtime looks for a class with aMainmethod to run when the program is executed. (More on that in a moment.) The nameProgram, though, is a common convention.public static void Main(string[] args)defines theMainmethod. This is the method that is called when the program is run. It takes an array of strings as an argument, which can be used to pass command-line arguments to the program.Console.WriteLine("Hello, World!");is a call to theWriteLinemethod of theSystem.Consoleclass. This method writes a line of text to the console, in this case, the string “Hello, World!”
Let’s go back and examine the short version of this program:
Console.WriteLine("Hello, World!");This version doesn’t define a class or a Main method. If you have a
module that contains only executable statements, as this one, the
compiler will automatically wrap them in a class called Program and
a Main method. This is called using top-level statements.3 Note
that while this is a convenient feature, in practice, there are
situations in which you will want to define a class and a Main method
explicitly, for example, if you want your Program class to be in a
custom namespace, or if you want to define other methods or properties
in the class.
Bootstrapping the development environment and project
You can play with this code in an online C# compiler or a C# REPL, but in a real project, you’ll want to create a repository with a solution file and a project in it.
The first thing you’ll need to do is to install the .NET SDK.4 (For the following, we’ll be using .NET 8, the latest LTS version at the time of this writing.)
Creating a solution and project
The next step is to create a solution and project. Usually, you would do
this using your favorite IDE or editor. Options include Visual Studio
Code, Microsoft Visual Studio, and Jetbrains Rider,5 all of which
provide a GUI through which to accomplish this task. For the purposes of
this article, we’re going to use the dotnet command-line interface.6
dotnet new sln --name HelloWorld --output HelloWorld
dotnet new console --name HelloWorld --output HelloWorld/HelloWorld
dotnet sln HelloWorld/HelloWorld.sln add HelloWorld/HelloWorld/HelloWorld.csprojLet’s break down these commands:
dotnet new sln --name HelloWorld --output HelloWorldcreates a new solution fileHelloWorld.slnin a subdirectory calledHelloWorld. This is the top-level solution directory, often also the repository root. The solution file collects together related projects. This command creates it empty, but we’re going to add ourHelloWorldproject to it below.dotnet new console --name HelloWorld --output HelloWorld/HelloWorldcreates a new console application project calledHelloWorld. The project is described in a fileHelloWorld/HelloWorld/HelloWorld.csproj. This directory structure may seem a little repetitive and redundant, but it is, unfortunately, a common naming strategy. Let’s break it down: The firstHelloWorldis the solution directory, which we created above. The second is the project directory, in which the project fileHelloWorld.csprojresides. This file specifies the project’s build parameters, package dependencies, and other settings. Theconsoletemplate also creates aProgram.csfile containing sample code for a console application, also included in the project directory.dotnet sln HelloWorld/HelloWorld.sln add HelloWorld/HelloWorld/HelloWorld.csprojadds theHelloWorld.csprojproject to theHelloWorld.slnsolution file.
Conveniently, the sample code in the generated Program.cs file looks
exactly like this:
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");Other common types of projects
The dotnet new command can create other types of projects and other
files. You can see a list of available templates by running
dotnet new --list. Some other useful templates include:
gitignoreto create a.gitignorefile suitable for .NET projects.globaljsonto create aglobal.jsonfile. This file specifies the version of the .NET SDK to use for the project.7classlibfor a class library. This project type omits theProgram.csfile and includes aClass1.csfile instead.xunitfor an xUnit test project.
There are also built-in templates for various types of applications, especially ASP.NET and Blazor web applications, and add-on “workloads” can provide additional templates.8
Building and running the project
So at this point, we can build and run the project:
dotnet run --project HelloWorld/HelloWorld/HelloWorld.csprojThis outputs the string “Hello, World!” on the console.
If you drill down to the HelloWorld/HelloWorld project directory,
you’ll now see subdirectories obj, containing intermediate files
generated by the compiler, and bin, containing the final application.
The .csproj file
Let’s take a closer look at HelloWorld.csproj, which we may want to
manually edit at some point.9
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>Breaking down this file:
<Project Sdk="Microsoft.NET.Sdk">specifies the SDK that the project uses. TheMicrosoft.NET.SdkSDK is used for most .NET projects, including console applications, class libraries, and unit test projects. There are other SDK’s for specific types of projects, such as ASP.NET web apps, daemons or background services, and desktop applications.<PropertyGroup>is a container for project properties that specify the project’s build parameters. As we further develop the project, we may end up adding one or moreItemGroupsections, which are used to specify package, project, and framework references, as well as custom handling for source files, such as data files that need to be copied to the output directory during the build process.<OutputType>Exe</OutputType>specifies that the project generates a console application. Other values includeLibraryfor a class library (the default), andWinExefor a Windows GUI application.<TargetFramework>net8.0</TargetFramework>specifies the version of .NET for which the project is built.<ImplicitUsings>enable</ImplicitUsings>enables implicit usings, which allows the project to use certainSystemnamespaces without explicitly importing them. Without this setting, ourProgram.cswould need to includeusing System;at the top, in order to access theSystem.Consoleclass.<Nullable>enable</Nullable>enables nullable reference types. That is, a reference variable will only be able to hold a null value if it is specifically declared as nullable (for example, by using a question mark after the C# type name, asobject?). This makes it easier, with the compiler’s help, to track which values may be null and to make null-reference exceptions less likely.
Resources
-
dotnetfiddle.net, an online C# compiler and runtime. ↩︎
-
“Download .NET” on Microsoft.com ↩︎
-
The following articles may be useful in getting started in various development environments:
- “Create a .NET console application using Visual Studio Code”
- “Create a .NET console application using Visual Studio”
- “Create projects and solutions” in JetBrains Rider
-
The dotnet new and dotnet sln commands ↩︎
-
".NET project SDKs", which also briefly describes the project-file format and includes links to resources that explore it more deeply. ↩︎