cv.cm

Coding · Programming languages

C# tutorial for beginners

6 min read

Short answer

C# is a statically typed, object-oriented language from Microsoft that runs on .NET, a free, cross-platform runtime for Windows, macOS and Linux. Install the .NET SDK, run dotnet new console, and dotnet run prints "Hello, World!". The core ideas are classes and records, generic collections such as List<T> and Dictionary<K, V>, LINQ queries, async/await, pattern matching and exceptions.

Open JSON formatter

What C# is and what it is used for

C# (pronounced "C sharp") was designed at Microsoft by a team led by Anders Hejlsberg and first released in 2002. It is standardized by Ecma (ECMA-334) and developed in the open together with .NET. C# is used for web APIs and sites (ASP.NET Core), Windows desktop apps, cross-platform mobile and desktop apps (.NET MAUI), cloud services, and games: the Unity engine uses C# for game scripts. Its syntax is in the C family, so it looks familiar to Java, C++ and JavaScript developers.

Install .NET and run Hello, world

Download the .NET SDK from dot.net and check it with dotnet --version. Create and run a console project:

dotnet new console -o HelloApp
cd HelloApp
dotnet run
# Hello, World!

The template creates Program.cs, which uses top-level statements, so no class or Main method is needed for a small program:

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

Variables and basic types

Every variable has a static type. var lets the compiler infer it from the value. Value types (numbers, bool, struct) hold their data directly; reference types (string, classes, arrays) refer to an object.

TypeExampleNotes
intint n = 42;32-bit integer
longlong big = 10_000_000_000;64-bit integer
doubledouble x = 3.14;64-bit floating point
decimaldecimal price = 19.99m;Base-10, good for money
boolbool ok = true;true or false
charchar c = 'A';UTF-16 code unit
stringstring s = "hi";Immutable text
var name = "Ada";
int count = 7;
double avg = count / 2.0;
Console.WriteLine($"{name} has {count} items, avg {avg}");  // Ada has 7 items, avg 3.5
Console.WriteLine(count / 2);                              // 3 (integer division)
Console.WriteLine(int.Parse("12") + 1);                    // 13
string? nickname = null;   // ? marks a nullable reference
Console.WriteLine(nickname ?? "none");                     // none

$"..." is an interpolated string. New projects enable nullable reference types, so the compiler warns when a value that may be null is used without a check.

Control flow and methods

int score = 72;
if (score >= 90) Console.WriteLine("A");
else if (score >= 70) Console.WriteLine("B");   // B
else Console.WriteLine("C");

for (int i = 0; i < 3; i++) Console.Write($"{Square(i)} ");  // 0 1 4
Console.WriteLine();

foreach (var fruit in new[] { "apple", "pear" }) Console.WriteLine(fruit);

string day = "SAT";
string kind = day switch
{
    "SAT" or "SUN" => "weekend",
    _ => "weekday",
};
Console.WriteLine(kind);   // weekend
Console.WriteLine(Greet("Lin"));                // Hello, Lin!

static int Square(int x) => x * x;
static string Greet(string name, string greeting = "Hello") => $"{greeting}, {name}!";

The switch expression and the or pattern are part of C# pattern matching. Methods can have default parameter values, named arguments (Greet(name: "Lin")) and expression bodies with =>.

Classes, properties and records

Classes have fields, properties (fields with get and set logic) and methods. A record is a type whose equality is based on its values, ideal for immutable data.

var acc = new Account("Ann");
acc.Deposit(50);
Console.WriteLine($"{acc.Owner}: {acc.Balance}");   // Ann: 50

var p1 = new Point(3, 4);
var p2 = p1 with { Y = 5 };                         // copy with one change
Console.WriteLine(p1);                              // Point { X = 3, Y = 4 }
Console.WriteLine(p1 == new Point(3, 4));           // True

public class Account
{
    public string Owner { get; }
    public decimal Balance { get; private set; }

    public Account(string owner) => Owner = owner;

    public void Deposit(decimal amount)
    {
        if (amount <= 0) throw new ArgumentException("amount must be positive");
        Balance += amount;
    }
}

public record Point(int X, int Y);

Type declarations must come after top-level statements in the same file. Classes support single inheritance (class Savings : Account) and any number of interfaces.

Collections and LINQ

Generic collections in System.Collections.Generic are the everyday data structures. LINQ (Language Integrated Query) adds methods such as Where, Select, OrderBy and GroupBy to every collection.

var nums = new List<int> { 3, 1, 2, 5 };
nums.Add(4);
nums.Sort();
Console.WriteLine(string.Join(", ", nums));      // 1, 2, 3, 4, 5

var ages = new Dictionary<string, int> { ["ann"] = 31, ["bob"] = 25 };
ages["cy"] = 40;
if (ages.TryGetValue("bob", out int bobAge)) Console.WriteLine(bobAge);  // 25

var evenSquares = nums.Where(n => n % 2 == 0).Select(n => n * n).ToList();
Console.WriteLine(string.Join(" ", evenSquares)); // 4 16

var oldest = ages.OrderByDescending(kv => kv.Value).First();
Console.WriteLine(oldest.Key);                    // cy

Other common collections are HashSet<T> for unique items, Queue<T>, Stack<T> and fixed-size arrays such as int[].

Signature feature: async and await

C# introduced the async/await model that many languages later adopted. An async method returns a Task and can await slow operations, such as web requests or file reads, without blocking a thread.

using System.Net.Http;

using var http = new HttpClient();
var sizes = await Task.WhenAll(
    GetLengthAsync(http, "https://example.com"),
    GetLengthAsync(http, "https://example.org"));
Console.WriteLine($"downloaded {sizes.Sum()} characters");

static async Task<int> GetLengthAsync(HttpClient http, string url)
{
    string html = await http.GetStringAsync(url);
    return html.Length;
}

Task.WhenAll runs both requests at the same time and waits for both. The using var declaration disposes the HttpClient at the end of the scope.

Exceptions

Errors are exceptions. Catch specific types first, and use finally or using for cleanup. The TryParse pattern avoids exceptions for input that is often invalid.

try
{
    int age = int.Parse("abc");
}
catch (FormatException ex)
{
    Console.WriteLine($"not a number: {ex.Message}");
}
finally
{
    Console.WriteLine("done");
}

if (!int.TryParse("41", out int parsed)) parsed = 0;
Console.WriteLine(parsed);   // 41

A small complete program: word frequency

Replace Program.cs in a console project with this code, then run dotnet run -- notes.txt.

using System.Text.RegularExpressions;

if (args.Length != 1)
{
    Console.Error.WriteLine("usage: dotnet run -- FILE");
    return 1;
}

try
{
    string text = File.ReadAllText(args[0]).ToLowerInvariant();
    var top = Regex.Matches(text, "[a-z']+")
        .GroupBy(m => m.Value)
        .Select(g => (Word: g.Key, Count: g.Count()))
        .OrderByDescending(t => t.Count)
        .Take(5);

    foreach (var (word, count) in top)
        Console.WriteLine($"{count,5}  {word}");
    return 0;
}
catch (FileNotFoundException)
{
    Console.Error.WriteLine($"file not found: {args[0]}");
    return 1;
}

Top-level statements can read command-line args and return an exit code. System.IO, System.Linq and System.Collections.Generic are imported automatically through implicit usings in new projects.

Common beginner mistakes

  • Using double for money; use decimal to avoid binary rounding errors.
  • Ignoring nullable warnings, which then become NullReferenceException at runtime.
  • Modifying a List<T> inside a foreach over the same list, which throws InvalidOperationException.
  • Creating a new HttpClient for every request in a long-running app. Reuse one instance or use IHttpClientFactory.
  • Forgetting that LINQ queries run lazily: the query runs again each time it is enumerated unless you call ToList().

Where to go next

Microsoft Learn has the official C# documentation, including a tour of the language and interactive tutorials. Then learn ASP.NET Core for web APIs, Entity Framework Core for databases, or Unity for games, and use xUnit or NUnit for tests. Visual Studio, VS Code with the C# Dev Kit, and JetBrains Rider are the common editors.

FAQ

Is C# only for Windows?

No. Modern .NET runs on Windows, macOS and Linux, and C# apps can be deployed to Linux servers and containers. Only some desktop UI frameworks, such as WPF and Windows Forms, are Windows-only.

What is the difference between C# and .NET?

C# is the programming language. .NET is the runtime and libraries that compile and run C# code (and also F# and Visual Basic).

Is C# similar to Java?

Yes, in many ways: both are statically typed, garbage-collected, object-oriented languages with C-style syntax. C# adds features such as properties, LINQ, async/await, value-type structs and records.

Is C# good for game development?

Yes. Unity, one of the most widely used game engines, uses C# for game scripts, and the Godot engine also supports C#.

All coding tutorials