Syntax and types
The project layout, top-level statements, the value and reference split, and nullability — the decisions that shape everything else.
A project, not just a file
C# compiles to an assembly that runs on the .NET runtime. The build is driven by a .csproj file, so the unit of work is a project rather than a single source file.
dotnet new console -o Hello
cd Hello
dotnet run # restore, build and execute
dotnet build -c Release # compile only
dotnet add package Humanizer # add a NuGet dependency
dotnet publish -c Release -r linux-x64 --self-contained// Program.cs — top-level statements, C# 9 onwards; no Main method required
using System;
using System.Collections.Generic;
using System.Linq;
var scores = new List<int> { 90, 85, 77 };
var average = scores.Average();
Console.WriteLine($"Average: {average:F1} across {scores.Count} scores");
record Score(int Value, string Subject); // an immutable data type
var best = new Score(90, "Maths");
Console.WriteLine(best); // Score { Value = 90, Subject = Maths }| Command | Purpose |
|---|---|
dotnet new console | Scaffold a project |
dotnet run | Restore, build and run |
dotnet test | Run the test project |
dotnet add package X | Add a NuGet dependency |
dotnet publish | Produce the deployable output |
Value types and reference types
A value type holds its data inline and is copied on assignment. A reference type holds a reference to an object on the heap, so assignment copies the reference and both names end up looking at the same object.
| Kind | Examples | Copy behaviour |
|---|---|---|
| Value type | int, double, bool, char, decimal, DateTime, struct, enum | A copy of the data |
| Reference type | string, class, arrays, List<T>, delegates | A copy of the reference |
record struct | A value-typed record | A copy of the data, with generated equality |
string | An immutable reference type | The reference is copied, but no method can change the text |
struct Point { public int X, Y; }
var a = new Point { X = 1, Y = 2 };
var b = a; // a full copy
b.X = 99;
Console.WriteLine(a.X); // 1 — a is untouched
var list1 = new List<int> { 1, 2 };
var list2 = list1; // the same object
list2.Add(3);
Console.WriteLine(list1.Count); // 3
// boxing turns a value into an object; unboxing casts it back and can throw
object boxed = 42;
int unboxed = (int)boxed;
// value types can be nullable, and ?? supplies a default
int? missing = null;
int actual = missing ?? 0;💡
Enable nullable reference types (
<Nullable>enable</Nullable> in the project file). The compiler then tracks which references may be null and warns where the bug would appear, instead of letting a NullReferenceException surface in production.Text and formatting
var name = "Ada";
var greeting = $"Hello, {name}! The time is {DateTime.Now:HH:mm}.";
// a verbatim string treats its contents literally: a quote is written twice
var title = @"She said ""hello"" and left.";
// a raw string literal (C# 11) needs no doubling at all
var json = """
{ "name": "Ada", "active": true }
""";
var upper = name.ToUpperInvariant(); // culture-independent
var same = string.Equals(name, "ada", StringComparison.OrdinalIgnoreCase);
var sb = new System.Text.StringBuilder();
for (var i = 0; i < 100; i++) sb.Append(i).Append(',');
var built = sb.ToString(); // one buffer, not a hundred strings$interpolates a string,@makes it verbatim, and the two combine as$@.- Strings are immutable, so every
+inside a loop allocates a new one; useStringBuilderfor repeated concatenation. - Compare with an explicit
StringComparison:OrdinalorOrdinalIgnoreCasefor identifiers and protocol values, and a culture-aware overload only for text shown to people. List<int>and other generic types are declared with angle brackets, and the compiler checks every use.
FAQ
Should I use a struct or a class?
A struct for a small value of a few fields that behaves like a number, such as money or a coordinate. A class for anything with identity, mutable state or inheritance. Default to a class until you have a reason not to.
Why does dividing two ints give 0?
Integer division truncates. Convert one operand first, for example
(double)total / count, or declare the variable as decimal when the value is money.Related
Classes and interfaces LINQ and async/await
Last refreshed 2026-09-18.