Cheat Sheet Library

C# Cheat Sheet

Microsoft's typed, object-oriented language for apps, games (Unity), web APIs and tools on .NET. This sheet targets C# 14 with .NET 10 and marks features that need a recent version.

  • 123 entries
  • 14 sections
  • Updated

123 entries

On this page

dotnet CLI #

CommandWhat it does
dotnet new console -n MyAppNew console app in the folder MyApp
dotnet new webapi -n MyApiNew ASP.NET Core Web API project
dotnet runBuild and run the project in the current folder
dotnet run app.csRun a single file without a project (.NET 10+)
dotnet watchRun and restart on every file change
dotnet build / dotnet publish -c ReleaseCompile / produce a release build to deploy
dotnet add package Newtonsoft.JsonAdd a NuGet package (.NET 10 also accepts dotnet package add)
dotnet testRun the tests (create a test project with dotnet new xunit)

Program Structure #

CodeWhat it does
Console.WriteLine("Hi");A complete Program.cs: top-level statements, no Main needed (C# 9+)
argsCommand-line arguments, available in top-level code
using System.Text.Json;Import a namespace; global using imports it for every file (C# 10+)
<ImplicitUsings>enable</ImplicitUsings>.csproj setting: System, System.IO, System.Linq, System.Collections.Generic and more are imported for you
namespace MyApp.Models;File-scoped namespace for the whole file (C# 10+)
static void Main(string[] args) { }Classic entry point inside a class (static async Task Main for async)

Types & Variables #

CodeWhat it does
int long double decimal bool char stringBuilt-in types; use decimal for money (19.99m): 0.1 + 0.2 == 0.3 is false for double
var count = 5;Type is inferred (still strongly typed)
const int Max = 10; / readonlyCompile-time constant / field set only in the constructor
int? age = null;Nullable value type; check with age.HasValue or age ?? 0
string? name = null;Nullable reference type: the compiler warns about unchecked use (<Nullable>enable</Nullable>, on in new projects)
name!Null-forgiving: "trust me, it is not null"
struct enum int boolValue types: copied on assignment
class string int[] List<T>Reference types: two variables can share one object
(string Name, int Age) p = ("Ann", 30);Tuple with named elements (p.Name); unpack with var (name, age) = p;
int.Parse("42") / int.TryParse(s, out int n)Text to number / safely, returns false on failure
(int)3.9 / nameof(age)Cast, truncates to 3 / variable name as a string

Strings #

CodeWhat it does
$"Hi {name}, you are {age}"String interpolation
$"{price:F2}" / $"{n:N0}" / $"{d:yyyy-MM-dd}"Format: 2 decimals / thousands separator / date
@"C:\temp\file.txt"Verbatim string: backslashes are literal
"""She said "hi"."""Raw string literal: no escaping needed (C# 11+)
$$"""{"name": "{{name}}"}"""Raw interpolated: {{ }} interpolates, single braces stay literal (C# 11+)
s.Length / s[0] / s[^1]Length / first / last character
s[..3] / s.Substring(1, 3)First 3 characters / 3 characters from index 1
s.ToUpper() / s.Trim() / s.Replace("a", "b")Change case / strip whitespace / replace
s.Contains("x") / s.StartsWith("x") / s.IndexOf("x")Search (IndexOf returns -1 if missing)
s.Split(',') / string.Join(", ", list)Split into an array / join into a string
string.IsNullOrWhiteSpace(s)Null, empty or only spaces
a.Equals(b, StringComparison.OrdinalIgnoreCase)Compare ignoring case
new StringBuilder().Append("a").ToString()Build long strings efficiently in loops

Operators & Patterns #

CodeWhat it does
user?.Address?.CityNull-conditional: null instead of NullReferenceException
name ?? "unknown" / ok ? "yes" : "no"Fallback when null / ternary
name ??= "guest";Assign only if null
customer?.Order = order;Null-conditional assignment: only assigns if customer is not null (C# 14)
obj is string s / obj as stringType test and cast in one step / cast or null
x is null / x is not nullNull checks that can't be fooled by an overloaded ==
n is > 0 and < 10Relational and logical patterns (C# 9+)
p is { Age: >= 18, Name: "Ann" }Property pattern
nums is [1, 2, ..]List pattern: starts with 1, 2 (C# 11+)
7 / 2 / 7 % 2 / 7 / 2.0Integer division (3) / remainder (1) / 3.5
&& / || / !And / or / not (short-circuit)

Control Flow #

string size = n switch          // switch expression (C# 8+)
{
    < 0 => "negative",
    0 => "zero",
    >= 1 and <= 9 => "small",
    _ => "large",               // _ = everything else
};
CodeWhat it does
if (a) { } else if (b) { } else { }Conditions
(a, b) switch { (0, 0) => "origin", _ => "other" }Switch on several values with a tuple
case > 100 when isVip:switch statement case with a pattern and a guard
for (int i = 0; i < 10; i++) { }Counting loop
foreach (var item in items) { }Loop over any collection (dictionary: foreach (var (key, value) in dict))
while (x) { } / do { } while (x);Condition loops (break, continue)

Methods #

CodeWhat it does
int Add(int a, int b) => a + b;Expression-bodied method
void Greet(string name = "World")Optional parameter; call with a named argument: Greet(name: "Ann")
bool TryGet(int id, out User user)out: method must set it; call with out var user
void Swap(ref int a, ref int b)ref: pass a variable by reference
int Sum(params int[] nums)Any number of arguments: Sum(1, 2, 3)
params ReadOnlySpan<int> numsparams with spans, lists and other collections (C# 13+)
Func<int, int> square = x => x * x;Lambda that returns a value (Action<T> returns nothing)
var add = (int a, int b = 1) => a + b;Lambda with inferred type (C# 10+) and a default (C# 12+)
static int WordCount(this string s)Extension method in a static class: "a b".WordCount()
extension(string s) { public int Words => s.Split(' ').Length; }Extension block in a static class, also allows properties (C# 14)

Classes & Interfaces #

public class Person(string name, int age)   // primary constructor (C# 12+)
{
    public string Name { get; } = name;
    public int Age { get; private set; } = age;

    public void Birthday() => Age++;
    public override string ToString() => $"{Name} ({Age})";
}
Person p = new("Ann", 30);                  // target-typed new (C# 9+)
CodeWhat it does
public / private / protected / internalEverywhere / this class / + subclasses / same project (members default to private)
class Admin : PersonInheritance; call the parent constructor with : base(...)
virtual / override / abstract / sealedCan be overridden / overrides / must be overridden / can't be inherited
interface IShape { double Area(); }Contract; implement with class Circle : IShape
new Order { Id = 1, Total = 9.95m }Object initializer: set properties after construction
class Box<T> { public T Value { get; set; } }Generic class: new Box<int>()
T Max<T>(T a, T b) where T : IComparable<T>Generic method with a constraint (also class, struct, new())

Records, Structs & Properties #

CodeWhat it does
public record Point(int X, int Y);Immutable data type with value equality and nice ToString() (C# 9+)
var p2 = p with { X = 5 };Copy with some values changed
public record struct Point(int X, int Y);Record as a value type (C# 10+)
public struct Size { public int W; public int H; }Small value type, copied on assignment
public enum Color { Red, Green, Blue }Named constants: Color.Red
public int Age { get; set; }Auto-property ({ get; private set; } = read-only from outside)
public string Id { get; init; }Settable only during object initialization (C# 9+)
public required string Name { get; init; }Caller must set it in the object initializer (C# 11+)
public string FullName => $"{First} {Last}";Computed read-only property
public string Name { get; set => field = value.Trim(); }field keyword: access the hidden backing field (C# 14)

Collections #

CodeWhat it does
int[] nums = [1, 2, 3];Array with a collection expression (C# 12+); new int[5] = 5 zeros, size: nums.Length
List<string> names = [];Growable list
names.Add("Ann") / names.Remove("Ann") / names.CountAdd / remove / size
names.Contains("Ann") / names.Sort() / names[0]Search / sort in place / by index
var ages = new Dictionary<string, int> { ["Ann"] = 30 };Key-value pairs
ages["Bob"] = 25;Add or overwrite (reading a missing key throws)
ages.TryGetValue("Ann", out int a) / ages.ContainsKey("Ann")Safe lookup / key check (also .Keys, .Values)
HashSet<int> seen = [1, 2]; / seen.Add(3)Unique values; Add returns false for duplicates
int[] all = [.. first, .. second, 99];Spread collections into a new one (C# 12+)

LINQ #

var names = people
    .Where(p => p.Age >= 18)
    .OrderBy(p => p.Name)
    .Select(p => p.Name)
    .ToList();
CodeWhat it does
.Where(x => x > 0) / .Select(x => x * 2)Filter / transform each item
.OrderBy(p => p.Age).ThenBy(p => p.Name)Sort (OrderByDescending for reverse)
.GroupBy(p => p.City)Groups with g.Key and the items in g
.First() / .FirstOrDefault()First item / or default (null, 0) instead of an exception
.Any(x => x > 5) / .All(x => x > 0)Does any / every item match
.Count() / .Sum() / .Average() / .Max()Aggregates
.Distinct() / .Take(5) / .Skip(10)Unique items / first 5 / skip 10
.ToList() / .ToArray() / .ToDictionary(p => p.Id)Run the query and store the result
.CountBy(p => p.City)Count per key (.NET 9+)
from p in people where p.Age >= 18 select p.NameQuery syntax: same as Where + Select, SQL-like

Async & Exceptions #

async Task<string> LoadAsync(string url)
{
    using var http = new HttpClient();
    return await http.GetStringAsync(url);
}
try { Console.WriteLine(await LoadAsync("https://example.com")); }
catch (HttpRequestException ex) { Console.WriteLine($"HTTP error {ex.StatusCode}"); }
catch (Exception ex) { Console.WriteLine(ex.Message); }
finally { Console.WriteLine("Done"); }
CodeWhat it does
Task / Task<T>Async method with no result / with a result
await Task.WhenAll(t1, t2)Wait for several tasks running at the same time
await Task.Delay(1000)Wait 1 second without blocking
await foreach (var x in GetItemsAsync())Loop over an IAsyncEnumerable<T>
throw new ArgumentException("Bad id");Throw an exception
throw;Rethrow inside catch, keeping the stack trace
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound)Exception filter: only catch when the condition is true
ArgumentNullException.ThrowIfNull(user);Guard clause (.NET 6+)
using var file = File.OpenRead(path);Disposed automatically at the end of the scope
class NotFoundException(string msg) : Exception(msg) { }Your own exception type

Useful .NET APIs #

CodeWhat it does
Console.ReadLine() / Console.Write("x")Read a line of input / print without a newline
File.ReadAllText(path) / File.WriteAllText(path, text)Read / write a whole file (also ReadAllLines, AppendAllText, ...Async)
File.Exists(path) / Directory.CreateDirectory(dir)Check / create a folder (with parents)
Path.Combine("data", "a.txt") / Path.GetExtension(path)Build a path safely / ".txt"
DateTime.Now / DateTime.UtcNow / DateTime.TodayNow (local) / now (UTC) / today at midnight
date.AddDays(7) / date.ToString("dd/MM/yyyy")Date math / format
(end - start).TotalDaysDifference is a TimeSpan
DateOnly / TimeOnlyJust a date / just a time (.NET 6+)
Math.Round(x, 2) / Math.Max(a, b) / Math.Clamp(x, 0, 10)Rounding / bigger value / limit to a range
Math.Sqrt(x) / Math.Pow(2, 10) / Math.Abs(x)Square root / power / absolute value
Random.Shared.Next(1, 7)Random int from 1 to 6 (upper bound is exclusive)
JsonSerializer.Serialize(obj) / JsonSerializer.Deserialize<T>(json)Object to JSON / back (System.Text.Json)

Tips #

  • Keep nullable reference types on and fix the warnings: they catch most NullReferenceExceptions before you run the code.
  • var x = [1, 2]; doesn't compile: collection expressions need a target type, so write int[] x = [1, 2]; or List<int> x = [1, 2];.
  • LINQ runs lazily: the query executes when you loop over it or call ToList(), and again every time you loop.
  • Never use .Result or .Wait() on a task in app code; await it, and use async void only for event handlers.
  • Math.Round(2.5) returns 2 (rounds to even); use Math.Round(x, MidpointRounding.AwayFromZero) for school rounding.