Create our first .NET Hello World

Now that we have the .NET SDK installed, we can use it to create our first application.

Create a console application

.NET provides project templates that give us a starting point for different types of applications.

We can see the available templates with:

dotnet new list

For our first application, we will use the Console App template:

dotnet new console -n HelloDotnet

This command has three important parts:

dotnet  new  console  -n HelloDotnet
  │      │      │          │
  │      │      │          └── Project name
  │      │      └───────────── Template
  │      └──────────────────── Create from a template
  └─────────────────────────── .NET CLI

Then we can enter the newly created directory:

cd HelloDotnet

What did .NET create?

Our project initially has a very simple structure:

HelloDotnet/
├── HelloDotnet.csproj
└── Program.cs

These two files have different responsibilities:

flowchart LR
    Project["HelloDotnet"]

    Code["Program.cs<br/>Our C# code"]
    Config["HelloDotnet.csproj<br/>How the project is built"]

    Project --> Code
    Project --> Config

Program.cs

Program.cs contains our C# source code.

A new console application contains something similar to:

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

For now, we only need to understand that this is the code our application will execute.

We will explore C# syntax in more detail in the next section.

.csproj

The .csproj file is the C# project file.

Open it and you will see something similar to:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

We don’t need to understand every property yet.

For now, the important idea is:

A project describes something .NET can build.

The .csproj file tells the .NET SDK about our project, including information such as its target framework, configuration, and dependencies.

For example:

<TargetFramework>net10.0</TargetFramework>

tells .NET that this project targets .NET 10.

As our application grows, the project file can also contain information about external packages and other projects it depends on.

We will return to the .csproj file later.

Build the application

We have source code and a project describing how it should be built.

Now we can ask the SDK to build it:

dotnet build

Conceptually:

flowchart LR
    Source["C# source code<br/>Program.cs"]
    Project["Project<br/>.csproj"]
    SDK[".NET SDK"]
    Output["Build output"]

    Source --> SDK
    Project --> SDK
    SDK --> Output

During the build, .NET compiles our source code and produces the files necessary to run the application.

After building the project, our directory will contain two additional directories:

HelloDotnet/
├── bin/
├── obj/
├── HelloDotnet.csproj
└── Program.cs

bin/

bin contains the final build output.

For example:

bin/
└── Debug/
    └── net10.0/
        ├── HelloDotnet
        ├── HelloDotnet.dll
        ├── HelloDotnet.deps.json
        └── HelloDotnet.runtimeconfig.json

These are files produced from our source code that are used to execute the application.

A useful mental model is:

**bin/** = build output

obj/

obj contains intermediate files that the build process uses while creating the final output.

These files help MSBuild and the .NET SDK perform the build efficiently.

For now, we can think of it as:

**obj/** = intermediate build files

Normally, we don’t manually edit files inside either bin/ or obj/.

flowchart TD
    Source["📄 <b>Source files</b><br/><span style='font-size:14px'>Program.cs</span>"]
    Build["⌨️ <b>dotnet build</b><br/><span style='font-size:14px'>.NET SDK<br/>(compiles the project)</span>"]
    Obj["📁 <b>obj/</b><br/><span style='font-size:14px'>Intermediate files<br/>(used during build)</span>"]
    Bin["📦 <b>bin/</b><br/><span style='font-size:14px'>Build output<br/>(compiled application)</span>"]

    Source --> Build
    Build --> Obj
    Build --> Bin

    style Source fill:#DDF4DD,stroke:#3F9855,stroke-width:2px
    style Build fill:#D8EDFF,stroke:#1683D8,stroke-width:2px
    style Obj fill:#FFF0B8,stroke:#E89B00,stroke-width:2px
    style Bin fill:#E9DFFF,stroke:#6040C8,stroke-width:2px

Run the application

Once our application can be built, we can run it:

dotnet run

And we should see:

Hello, World!

dotnet run is convenient during development because it handles the build when necessary and then runs the application.

Conceptually:

flowchart LR
    Code["Program.cs"] --> Build["Build"]
    Project[".csproj"] --> Build
    Build --> App["Application"]
    App --> Output["Hello, World!"]

The complete picture

We have now gone through the basic lifecycle of a .NET project:

flowchart LR
    Create["Create<br/><code>dotnet new</code>"]
    Code["Write<br/>C#"]
    Build["Build<br/><code>dotnet build</code>"]
    Run["Run<br/><code>dotnet run</code>"]

    Create --> Code --> Build --> Run

And our project has four important elements:

ElementPurpose
Program.csContains our C# source code
.csprojDescribes the project and how .NET should build it
obj/Contains intermediate build files
bin/Contains the final build output

The most important distinction at this point is:

  • Program.cs - What should my application do?
  • .csproj - What am I building and how should .NET build it?

Now that we understand where our code lives and how .NET builds it, we can start learning the C# language itself.