1.3. C# Program Structure#

1.3.1. The .NET Templates#

The .NET SDK comes with built-in templates for creating projects and files, including console apps, class libraries, unit test projects, etc. For learning purposes, we will mainly use the console app template. To see the list of templates, you may issue the dotnet new list command:

PS C:\> dotnet new list
These templates matched your input:

Template Name                       Short Name      Language    Tags 
----------------------------------  --------------  ----------  ------------------------
API Controller                      apicontroller   [C#]        Web/ASP.NET 
ASP.NET Core Empty                  web             [C#],F#     Web/Empty 
ASP.NET Core Web API                webapi          [C#],F#     Web/Web API/API/Service 
...
ASP.NET Core Web App (Razor Pages)  webapp,razor    [C#],F#     Web/MVC/Razor Pages 
Blazor Web App                      blazor          [C#]        Web/Blazor/WebAssembly 
Class Library                       classlib        [C#],F#,VB  Common/Library 
Console App                         console         [C#],F#,VB  Common/Console 
...

1.3.2. A Console App#

We can try out the Console App template, which will give us a “Hello, World” message when executing. In the terminal, let us change directory (cd) into the test directory and issue command dotnet new console. The .NET SDK will generate a project for us, which will include the following files and directories:

  1. The Program.cs file

  2. The obj folder

  3. The testPrj.csproj file

  4. Also, a bin directory will be created later after the project is built.

PS C:\[username]> cd test
PS C:\[username]\test> dotnet new console
The template "Console App" was created successfully.

Processing post-creation actions...
Restoring C:\Users\[username]\test\test.csproj:
Restore succeeded.


PS C:\Users\[username]\test> ls
    Directory: C:\Users\[username]\test

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----         3/12/2026  12:40 PM                obj
-a----         3/12/2026  12:40 PM             40 Program.cs
-a----         3/12/2026  12:40 PM            253 test.csproj

PS C:\Users\[username]\test>

From the terminal we can open vscode by typing code . and hit Enter (the dot . means the current directory; namely, open VS Code and use this current directory as project folder).

PS C:\Users\tcn85\test> code .
PS C:\Users\tcn85\test>

VS Code will open. We then choose the Explorer () view and click on the Program.cs file. You will see that the Program.cs looks simple as below and we are working the executable code directly.

1Console.WriteLine("Hello, World!");

The template is very succinct because of after C#9 there’s a feature called top-level statements, with which you write and execute code without the boilerplate class and Main method wrapper because he compiler automatically generates a Program class with an entry point method for the application and adds a set of implicit global using directives such as Microsoft.NET.Sdk to include the most common namespaces.

While top-level statements are friendly to new users and is the default when you create a new project using dotnet new console, there’s a limit that only one file in a project can have top-level statements. Therefore, as we learn more about C# programming, you will need to learn how to structure your code using namespaces and classes.

Important

There can be only one entry point (the Main() method) per C# project, but a project can have many .cs files.

A .csproj file defines a project, and a project can only have one entry point — either one file with top-level statements, or one static void Main() method. However, a project can contain as many .cs files as you like; all extra .cs files just define additional classes or helpers that the entry point can use. If you have two programs that each need their own Main(), they must be two separate projects (two separate .csproj files).

1.3.3. C# Program Structure#

1.3.3.1. Creating a C# Project#

To use the conventional C# program style, you use the --use-program-main option to create a console app project with the Main method. In the example below, we create a test2 folder, change into the test2 folder, then issue dotnet new console with the option --use-program-main to create the project with conventional Program.cs structure:

PS C:\Users\[username]> mkdir test2
    Directory: C:\Users\[username]
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----         3/13/2026  12:12 AM                test2

PS C:\Users\[username]> cd test2
PS C:\Users\[username]\test2> dotnet new console --use-program-main
The template "Console App" was created successfully.

Processing post-creation actions...
Restoring C:\Users\[username]\test2\test2.csproj:
Restore succeeded.

After creating the project, we first `ls` (list) the folder to see what files and directories are there.

```powershell
PS C:\Users\[username]\test2> ls
    Directory: C:\Users\[username]\test2
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----         3/13/2026  12:12 AM                obj
-a----         3/13/2026  12:12 AM            140 Program.cs
-a----         3/13/2026  12:12 AM            253 test2.csproj

This will give us the same project files as running dotnet new console without the --use-program-main option, but the Program.cs file will be different. Opening the Program.cs and you see the template code as:

namespace testPrj;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello, World!");
    }
}

The boilerplate code above actually means we formally wrap our C# code around in layers when our project scale go beyond a simple script:

┌─────────────────────────────────────────┐
│  namespace testPrj                      │
│  ┌───────────────────────────────────┐  │
│  │  class Program                    │  │
│  │  ┌─────────────────────────────┐  │  │
│  │  │  static void Main(string[]) │  │  │
│  │  │  {                          │  │  │
│  │  │      // statements here     │  │  │
│  │  │  }                          │  │  │
│  │  └─────────────────────────────┘  │  │
│  └───────────────────────────────────┘  │
└─────────────────────────────────────────┘

1.3.3.2. The Program.cs#

Some important concepts that you need to learn from this template code example here so we have better ideas about the basic structure of C# programs.

  1. using System;:

    Starting .NET 6, using System is implicit as defined in the .csproj file so it is not shown in the Program.cs file. We should know that, System is a root-level namespace that contains basic defined value and reference types. The using directive allows you to use the types defined in a namespace without specifying the fully qualified namespace of that type. For example, Console is a class inside the System namespace and we use its WriteLine method to print to the console by typing Console.WriteLine(), less the System part.

    using System;
    Console.WriteLine("Hello");  // instead of System.Console.WriteLine
    
  1. namespace: The namespace keyword is used to declare a scope to organize types (such as classes). For example, following the template’s program structure, we may define a namespace as below to contain unique code elements such as class, interface, struct, which will be implemented to create functionalities for the application.

        namespace SampleNamespace
        {
            class SampleClass { }
            interface ISampleInterface { }
            struct SampleStruct { }
            enum SampleEnum { a, b }
            delegate void SampleDelegate(int i);
            namespace Nested
            {
                class SampleClass2 { }
            }
        }
    

    An example of namespace is the System namespace in .NET, which defines some of the fundamental types. When we run Console.WriteLine("Hello, World");, we are actually running System.Console.WriteLine("Hello World!"); We do not specify “System” because it is “imported” (by the hidden using System) already and we can use the fundamental features (e.g., Console, String, Math, etc) within the namespace. Software engineers use namespace the same way to organize the functionalities in applications. [1]

  2. class: A class is a blueprint for creating objects. It is a user-defined reference type that defines:

    • fields/properties (data that describe the object), and

    • methods (functions that define the object’s behavior).

    By convention, each class is defined in its own file, named after the class (e.g., MyClass.cs).

  3. The Main method: The Main method is the entry point of a C# application and therefore the first method invoked when an application is executed. There is only one entry point in a C# program.

  4. method: A method (function) is an object-oriented term for function, which is a series of statements designed to perform certain task. In C#, just like Java, the Main method is the entry point of the program, meaning it is the first method invoked when a program is executed.

  5. static & void:

    • The modifier static means the method can be called without creating a new object from the class.

    • void means the Main method does not return anything.

  6. string[] args: The args are called “command line arguments” and in this example the type is string array; meaning when calling this method you send the arguments in and they are zero-indexed as an array.

1.3.3.3. Top-Level Statements#

When you create a real console project with dotnet new console (without the option --use-program-main), the generated Program.cs uses top-level statements (the default since .NET 6), which lets you write code without explicitly declaring class Program and Main. You still create a project and it still compiles into a proper executable, but the compiler automatically wraps the top-level statements into an entry point, which is equivalent to the explicit class Program / static void Main. As you might have noticed, the Program.cs

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

1.3.3.4. Script Mode#

When you enable the Live Code mode of the page, you can write and run C# inside the code cells (but not markdown cells), the .NET Interactive kernel, similar to csharprepl, runs code in script mode: you write statements directly without creating a project wrapping code in a namespace, class, or Main method. This is similar to how Python or JavaScript work in their REPL.

Script mode (notebook)

Top-level statements (project)

Explicit Main (project)

namespace declaration

not supported

optional

optional

class Program / Main

can do but not needed

can do but not needed

required

Compiles to .exe

interpreted/runs in-memory

supported

supported

How to create

.NET Interactive notebooks

dotnet new console (default)

dotnet new console --use-program-main

In this book, notebooks use script mode for interactive exploration, while the companion C# projects use top-level statements or the explicit Main form depending on context.

1.3.4. Solutions and Projects#

For simple project, you can create a project without creating a solution. To do that, create a directory in command line, and issue the command such as dotnet new console --use-program-main to use the .NET templates for creating new projects. A .csproj file will be created and you know that this is a project folder.

For more complicated projects, the .NET platform uses solutions and projects to organize code items in specific structure. A solution is a container or workspace for one or more projects, and each project would contain source code files. A web app solution, for example, may include a website project, a database project, and a server-side API project; and each of the project will be named differently under different project folders inside the solution directory.

┌────────────────────────────────────────────────────────────────────┐
│                            MySolution.sln                          │
│                   Solution file – groups all projects              │
│                                                                    │
│ ┌────────────────┐  ref  ┌────────────────┐ ref ┌────────────────┐ │
│ │  MyApp.API     │ ────▶ │  MyApp.Core    │◀────│  MyApp.Infra   │ │
│ │                │       │                │     │                │ │
│ │ .csproj        │       │ .csproj        │     │ .csproj        │ │
│ │ Controllers,   │       │ Models,        │     │ DB,            │ │
│ │ endpoints      │       │ interfaces     │     │ repositories   │ │
│ └────────────────┘       └───────▲────────┘     └────────────────┘ │
│                               ref│                                 │
│                         ┌────────┴───────┐                         │
│                         │  MyApp.Tests   │                         │
│                         │                │                         │
│                         │ .csproj        │                         │
│                         │ Unit &         │                         │
│                         │ integration    │                         │
│                         │ tests          │                         │
│                         └────────────────┘                         │
└────────────────────────────────────────────────────────────────────┘

To create a solution as a workspace, we use the command dotnet new sln in the solution directory. You then create all your project directories in the solution directory, and then create a new project in each of the project directories using the .NET templates (e.g., Console App or Web App).

MySolution/
├── MySolution.sln
├── MyApp/
│   └── MyApp.csproj
├── MyLibrary/
│   └── MyLibrary.csproj
└── MyTests/
    └── MyTests.csproj

To manage your projects and solutions, you can:

  1. Create a new solution

    dotnet new sln -n MySolution
    
  2. Create a project

    dotnet new console -n MyApp
    dotnet new classlib -n MyLibrary
    
  3. Add projects to the solution

    dotnet sln MySolution.sln add MyApp/MyApp.csproj
    dotnet sln MySolution.sln add MyLibrary/MyLibrary.csproj
    
  4. Common solution commands for further management of projects and solutions include:

Task

Command

List projects in solution

dotnet sln list

Remove a project

dotnet sln remove MyApp/MyApp.csproj

Add project reference

dotnet add MyApp reference MyLibrary

Build entire solution

dotnet build MySolution.sln

Run a specific project

dotnet run --project MyApp

Add a NuGet package

dotnet add MyApp package Newtonsoft.Json

If you create a project in a folder using VS Code’s “Create .NET Project” button in the Explorer view, a solution will be created with the same name as the project. To manage solutions, the Solution Explorer in VS Code can be used, with the C# Dev Kit extension installed. The Solution Explorer panel in the sidebar allows you to:

  • Add/remove projects

  • Manage NuGet packages

  • View project references

  • Run/debug projects [2].

1.3.5. Comments, Curly Braces, and Semicolon#

Comments are important as they make code more readable. C# offers single-line and multiple-line comments:

Single-line Comments: Single-line comments start with two forward slashes (//). Any text between // and the end of the line is ignored by C# (will not be executed).

Multi-line Comments: Multi-line comments start with /* and ends with */. Any text between /* and */ will be ignored by C#.

The curly braces { } in C# mark the beginning and the end of a block of code.

Semicolons work as statement terminator character in C# and are required because they prevent syntax ambiguities. Semicolons after method or accessor block, however, is not allowed.

1.3.5.1. Compound Statements (Code Blocks)#

A code block (or simply “block”), also referred to as a compound statement, is the grouping of multiple statements into a single statement. While each statement that ends with a semicolon, often it makes sense to see a block of statements as one lexical unit. In C#, a code block is delimited by a pair of curly braces to include a list of statements. For example, the following code shows an if statement, you can see the two highlighted code blocks being compound statements because the statements inside the curly braces. More specifically, line 4-9 is a compound statement of the if statement.

double radius = 5.0;  // try a negative value to test the else branch
double area;
if (radius >= 0)
{
    // calculate the area of the circle
    area = Math.PI * radius * radius;
    Console.WriteLine($"The area of the circle is: {area:0.00}");
}
else
{
    Console.WriteLine($"{radius} is not a valid radius.");
}
The area of the circle is: 78.54

Footnotes