With your development environment now set up, it's time to create your first real project: BiblioTechConsole, the first building block of the BiblioTech system we'll build throughout the course. In this lesson you'll create the project step by step, write and run a "Hello World", and we'll analyze line by line every part of the code, covering both the classic form and the modern (top-level statements) form of writing it. Understanding this minimal structure well is essential, because it will be the foundation all the code you write in the coming modules will rest on.
Contents
- Creating the BiblioTechConsole project step by step
- Writing and running the first Hello World
- Line-by-line explanation of the classic version
- The modern variant: top-level statements
- Building and running with
dotnet run
Creating the BiblioTechConsole project step by step
Open a terminal in the folder where you want to keep your course projects and run:
As we saw in the previous lesson, this command creates a BiblioTechConsole folder with the
basic structure of a console project:
Open the BiblioTechConsole folder with your editor (VS Code, Visual Studio, or Rider) and
locate the Program.cs file. By default, this file contains a small sample program generated
automatically by the template:
This is the modern version (with top-level statements) that dotnet new console generates
in current SDK versions. Let's modify it to talk about BiblioTech instead:
You can remove the automatically generated comment (the line starting with //); it isn't
needed for the program to work, it's just informational text added by the template.
Writing and running the first Hello World
Save the Program.cs file with the content above and, from the terminal, inside the project
folder, run:
You should see in the terminal:
Congratulations! You've just written and run your first C# program. Simple as it may look, this small program already puts several fundamental language concepts into play, which we'll now dissect.
Line-by-line explanation of the classic version
Before top-level statements were introduced (starting with C# 9, in 2020), every C# console program had to be written with a more explicit structure. It's very important to know this classic form, because:
- It's what you'll find in a huge amount of existing code, documentation, and older tutorials.
- It's actually what the compiler generates "underneath" even when we use the modern, simplified form.
Here's how our same program would look in its classic, complete form:
using System;
namespace BiblioTechConsole
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Welcome to BiblioTechConsole!");
}
}
}Let's explain each line, from top to bottom:
| Line | What it is | Explanation |
|---|---|---|
using System; |
using directive |
Indicates that this file will use types from the System namespace, where, among other things, the Console class lives. Without this line, we would have to write System.Console.WriteLine(...) every time. |
namespace BiblioTechConsole |
Namespace declaration | A namespace groups related code under a common name, avoiding conflicts with classes of the same name defined in other libraries. It usually matches the project's name. |
class Program |
Class declaration | In C#, all executable code lives inside a class. By convention, the class containing the program's entry point is called Program. We won't go deep into what a class is yet: that comes in Module 3; for now it's enough to know it's the mandatory "container" for the code. |
static void Main(string[] args) |
The Main method |
This is the program's entry point: the first and only instruction the CLR runs on startup. static means it can be called without needing an object of type Program (an OOP topic, Module 3); void indicates the method returns no value; string[] args is an array of command-line arguments, which we'll cover in the next lesson. |
Console.WriteLine("..."); |
Statement | The only "real" line of code in the program: it writes the given text to the console and moves to a new line. |
The braces { } |
Block delimiters | Each { opens a block of code and its matching } closes it. The namespace, the class, and the Main method are each a block delimited by braces. |
It's important to notice the nesting: the namespace contains the class, the class contains
the Main method, and the Main method contains the Console.WriteLine statement. This
hierarchy of braces is the general structure of (almost) any .cs file you'll write, and we'll
revisit it in more detail in the next lesson, "Basic Syntax and Structure".
The modern variant: top-level statements
Since C# 9, the language lets you omit all the "scaffolding" code (namespace, class, Main
method) when the program is simple, leaving only the statements that actually matter. This is
called top-level statements. It's exactly what dotnet new console generated when it
created your project:
So what happened to everything else? The C# compiler still generates the same structure
underneath (namespace, class Program, static void Main) automatically; you simply don't
have to write it yourself. It's exactly the same program, just with less boilerplate to write
and read.
| Aspect | Classic form | Top-level statements |
|---|---|---|
| Code you have to write | Namespace + class + Main method + statements |
Only the statements |
| Compiled result | Identical | Identical |
| When to use it | Large programs with several classes, or when more than one explicit entry point is needed | Simple console programs, scripts, learning projects |
| Available since | Always | C# 9 (2020) onward |
By default, in this course we'll use top-level statements for simple console examples
(especially in this first module), because they let us focus on the concept being learned
without getting distracted by boilerplate code. When we get to Object-Oriented Programming
(Module 3), where we'll define our own classes (Book, Member, Loan), it will again become
common to see structures closer to the classic form, combined with Main as the entry point.
Can I use args (command-line arguments) with top-level statements?
Yes. Even though string[] args isn't declared explicitly, the system makes an implicit
variable called args available to you if you need it:
if (args.Length > 0)
{
Console.WriteLine($"Hello, {args[0]}!");
}
else
{
Console.WriteLine("Welcome to BiblioTechConsole!");
}Don't worry if you don't yet understand if or the $"..." interpolation: we'll cover them in
detail in Modules 2 and 6 respectively (control flow and strings). This is just so you know that
the args variable is still available even though the Main method isn't visible explicitly.
Building and running with dotnet run
We already used dotnet run to run our program, but it's worth understanding exactly what
happens "under the hood" when we launch it:
- Dependency restoration:
dotnetchecks whether the project needs to download any external packages (none, in our case, so far) and prepares them if needed. - Compilation: the C# compiler translates the source code (
Program.cs) into intermediate language (IL), generating an executable inside thebin/folder. - Execution: the CLR loads that executable and runs it, compiling the IL to machine code on the fly (JIT compilation, mentioned in the first lesson).
If you just want to build without running (for example, to check there are no errors without seeing the output on screen), you can use:
And if you want to run the already-built executable directly (without going through the whole
build process again), you can find it inside bin/Debug/net8.0/ and run it straight from
there. In practice, while learning, dotnet run is the most convenient way, and it's what we'll
use throughout the course.
Editing and re-running
A common workflow while learning is: edit Program.cs, save, and run dotnet run again. For
example, try adding a second line to your program:
Running dotnet run again, you'll see both lines in the terminal, one below the other, because
WriteLine always adds a line break at the end of whatever it writes.
Common Mistakes and Tips
- Forgetting the semicolon
;at the end of a statement: this is one of the most frequent mistakes when starting out. The compiler will show an error message pointing to the exact line. - Saving the file but running from the wrong folder:
dotnet runmust be run from the project folder (where the.csprojfile is), not from a folder above or below it. - Thinking you always have to write out the full classic form: that's not the case; for
simple programs, top-level statements are perfectly valid, and in fact are the form the
official
dotnet new consoletemplate generates. - Confusing
Console.WritewithConsole.WriteLine:Writewrites the text without moving to a new line;WriteLinedoes move to a new line at the end. If two consecutive messages appear "stuck together" on the same line, you probably usedWriteinstead ofWriteLine. - Tip: when the compiler shows an error, read it calmly: it usually points to the file, the line, and a fairly precise description of what's wrong (for example, "a semicolon is missing" or "that name could not be found").
Exercises
-
Create the
BiblioTechConsoleproject by following the steps in this lesson (if you haven't already) and modifyProgram.csso that, using top-level statements, it writes two lines: one with the system's name ("BiblioTechConsole") and another with a welcome message of your choice. Run the program withdotnet runand check the result. -
Rewrite the program from the previous exercise using the full classic form (
using,namespace,class Program,static void Main), keeping the same behavior (the same two output lines). Run it and check that the on-screen result is identical to exercise 1. -
Using the implicit
argsvariable in the top-level statements version, write a program that shows"Welcome to BiblioTechConsole!"if no argument is passed, or a personalized greeting if one is passed. (Hint: in most editors you can configure command-line arguments in the debug/run options; if you don't know how to do that yet, don't worry, just focus on getting the code to compile correctly).
Solutions
-
Contents of
Program.cs:Console.WriteLine("BiblioTechConsole"); Console.WriteLine("Welcome to the library management system!");Running
dotnet runshould show both lines in the terminal, one below the other. -
Equivalent classic version:
using System; namespace BiblioTechConsole { class Program { static void Main(string[] args) { Console.WriteLine("BiblioTechConsole"); Console.WriteLine("Welcome to the library management system!"); } } }The on-screen result is exactly the same as in exercise 1, because both forms compile, essentially, to the same program.
-
Sample solution:
if (args.Length > 0) { Console.WriteLine($"Hello, {args[0]}! Welcome to BiblioTechConsole."); } else { Console.WriteLine("Welcome to BiblioTechConsole!"); }If the program runs with no arguments, the generic message is shown; if an argument is passed (for example, a name), the personalized greeting is shown. There's no need to fully understand
ifor$"..."interpolation yet: we'll cover them in detail later in the course.
Conclusion
In this lesson you created your first real project of the course, BiblioTechConsole, and
learned to tell apart the classic structure of a C# program (namespace, class, Main
method) from the modern form with top-level statements, understanding that both produce
exactly the same result. You also practiced the full cycle of editing and running with
dotnet run.
Now that you know how to create and run programs, it's time to dig into the syntax rules that govern any C# file: case sensitivity, blocks delimited by braces, comment types, and naming conventions. That's exactly what we'll cover in the next lesson, Basic Syntax and Structure.
C# Programming Course
Module 1: Introduction to C#
- Introduction to C#
- Setting Up the Development Environment
- Hello World Program
- Basic Syntax and Structure
- Variables and Data Types
- Arrays and Strings
Module 2: Control Structures
Module 3: Object-Oriented Programming
- Classes and Objects
- Methods
- Constructors and Destructors
- Inheritance
- Polymorphism
- Encapsulation
- Abstraction
- Structs and Records: Value Types and Reference Types
Module 4: Advanced C# Concepts
- Interfaces
- Delegates and Events
- Pattern Matching and Modern C# Features
- Generics
- Collections
- LINQ (Language Integrated Query)
- Asynchronous Programming
Module 5: Working with Data
- File I/O
- Serialization
- Database Connectivity
- Entity Framework
- Working with JSON and Consuming REST APIs
Module 6: Advanced Topics
- Reflection
- Attributes
- Dynamic Programming
- Memory Management and Garbage Collection
- Multithreading and Parallel Programming
Module 7: Building Applications
Module 8: Best Practices and Design Patterns
- Coding Standards and Best Practices
- Design Patterns
- Dependency Injection and Inversion of Control
- Unit Testing
- Code Review and Refactoring
