C#
Printed from:
C# Cheatsheet
Language Overview
C# is a modern, object-oriented programming language developed by Microsoft, part of the .NET ecosystem. It is statically-typed, type-safe, and designed for building robust and scalable enterprise applications across web, mobile, desktop, cloud, and gaming platforms. The current stable release is C# 14 (with .NET 10, LTS).
Key Characteristics
- Developed by Microsoft and maintained as an open-source language
- Part of the unified .NET platform (formerly .NET Framework / .NET Core)
- Strongly-typed with nullable reference types enabled by default in new projects
- Supports object-oriented, functional, and component-oriented programming paradigms
- Runs on the Common Language Runtime (CoreCLR) or compiled ahead-of-time with NativeAOT
Basic Syntax
Hello World Example
Modern C# supports top-level statements, file-scoped namespaces, and implicit using directives, eliminating most boilerplate:
123// Program.cs — top-level statements (C# 9+)
Console.WriteLine("Hello, World!");
The traditional form is still valid:
12345678910namespace MyApp; // file-scoped namespace (C# 10+)
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}
Naming Conventions
- Classes / Records / Structs: PascalCase (
MyClass) - Methods: PascalCase (
DoSomething()) - Local variables / parameters: camelCase (
myVariable) - Constants and static readonly: PascalCase (
MaxValue) - Interfaces: Start with 'I' (
IDisposable) - Private fields:
_camelCase(_count)
Data Types
Primitive Types
1234567891011121314151617181920212223// Integral Types
byte b = 255; // 8-bit unsigned integer
sbyte sb = -128; // 8-bit signed integer
short s = 32_767; // 16-bit signed integer (digit separators allowed)
ushort us = 65_535; // 16-bit unsigned integer
int i = 2_147_483_647; // 32-bit signed integer
uint ui = 4_294_967_295; // 32-bit unsigned integer
long l = 9_223_372_036_854_775_807; // 64-bit signed integer
ulong ul = 18_446_744_073_709_551_615; // 64-bit unsigned integer
nint ni = 0; // native-sized signed integer
nuint nui = 0; // native-sized unsigned integer
// Floating-Point Types
float f = 3.14f; // 32-bit floating-point
double d = 3.14159; // 64-bit floating-point
decimal m = 3.14159m; // 128-bit high-precision decimal
Half h = (Half)1.5; // 16-bit floating-point
// Other Types
char c = 'A'; // Single 16-bit Unicode character
bool boolean = true; // Logical true/false
string str = "Hello"; // String of Unicode characters
Nullable Reference Types
Nullable reference types are enabled by default in new projects (<Nullable>enable</Nullable>):
12345678910111213string nonNull = "value"; // Cannot be null
string? maybeNull = null; // May be null
int? nullableInt = null; // Nullable value type
// Null-coalescing operators
int definiteValue = nullableInt ?? 0;
maybeNull ??= "default"; // Assign only if null
// Null-conditional / null-conditional assignment (C# 14)
int? length = maybeNull?.Length;
maybeNull?.Trim();
person?.Name ??= "Unknown"; // C# 14: null-conditional assignment
Value vs Reference Types
12345678910111213141516171819202122// Value Types (stored on the stack or inline)
readonly struct Point
{
public int X { get; init; }
public int Y { get; init; }
}
// record struct — value type with value equality
public record struct Vector(double X, double Y);
// Reference Types (stored on the heap)
class Person
{
public required string Name { get; init; } // C# 11+
}
// record class — reference type with value equality
public record Customer(string Name, string Email);
// ref struct — stack-only types (e.g., Span<T>)
ref struct Buffer { /* ... */ }
Variables and Constants
Variable Declarations
123456789101112131415// Explicit typing
int age = 30;
// Implicit typing
var name = "John"; // Compiler infers type
// Target-typed new (C# 9+)
List<string> items = new();
// Constants
const double PI = 3.14159;
// Type aliases for any type (C# 12+)
using IntList = System.Collections.Generic.List<int>;
Operators
Arithmetic Operators
1234567int a = 10, b = 5;
int sum = a + b; // Addition
int diff = a - b; // Subtraction
int prod = a * b; // Multiplication
int div = a / b; // Division
int mod = a % b; // Modulus
Comparison Operators
1234567bool result = (a == b); // Equal to
bool notEqual = (a != b);// Not equal to
bool greater = (a > b); // Greater than
bool less = (a < b); // Less than
bool greaterEqual = (a >= b); // Greater or equal
bool lessEqual = (a <= b); // Less or equal
Logical Operators
12345bool x = true, y = false;
bool andResult = x && y; // Logical AND
bool orResult = x || y; // Logical OR
bool notResult = !x; // Logical NOT
Control Structures
Conditional Statements
12345678910111213141516171819202122232425262728293031323334353637383940// If-Else
if (condition)
{
// Code block
}
else if (anotherCondition)
{
// Alternative block
}
else
{
// Default block
}
// Ternary Operator
string result = (age >= 18) ? "Adult" : "Minor";
// Switch Statement
switch (variable)
{
case value1:
// Code
break;
case value2:
// Code
break;
default:
// Default code
break;
}
// Switch Expression (C# 8+)
string label = shape switch
{
Circle { Radius: > 0 } c => $"Circle r={c.Radius}",
Square s when s.Side > 0 => "Square",
null => "Nothing",
_ => "Unknown"
};
Pattern Matching
12345678910111213// Type, property, and relational patterns
if (obj is Person { Age: >= 18, Name: var n })
{
Console.WriteLine($"{n} is an adult");
}
// List patterns (C# 11+)
int[] data = { 1, 2, 3 };
if (data is [1, .., var last])
{
Console.WriteLine($"Starts with 1, ends with {last}");
}
Loops
12345678910111213141516171819202122232425262728293031// For Loop
for (int i = 0; i < 10; i++)
{
// Repeated code
}
// While Loop
while (condition)
{
// Repeated code
}
// Do-While Loop
do
{
// Code executed at least once
} while (condition);
// Foreach Loop
int[] numbers = [1, 2, 3, 4, 5];
foreach (int num in numbers)
{
Console.WriteLine(num);
}
// Await foreach (async streams, C# 8+)
await foreach (var item in GetItemsAsync())
{
Console.WriteLine(item);
}
Loop Control
12345678910111213// Break: Exit loop
for (int i = 0; i < 10; i++)
{
if (i == 5) break;
}
// Continue: Skip current iteration
for (int i = 0; i < 10; i++)
{
if (i % 2 == 0) continue;
// Process odd numbers
}
Functions
Basic Function
1234567891011121314151617181920212223242526272829// Method with return type
public int Add(int a, int b)
{
return a + b;
}
// Expression-bodied member
public int Multiply(int a, int b) => a * b;
// Void method (no return)
public void PrintMessage(string message)
{
Console.WriteLine(message);
}
// Optional and Named Parameters
public void Greet(string name, string greeting = "Hello")
{
Console.WriteLine($"{greeting}, {name}!");
}
// params collections (C# 13+) — accept Span<T>, IEnumerable<T>, etc.
public int Sum(params ReadOnlySpan<int> values)
{
int total = 0;
foreach (var v in values) total += v;
return total;
}
Lambda Expressions
1234567891011// Lambda for simple operations
Func<int, int, int> add = (x, y) => x + y;
int result = add(3, 4); // result = 7
// Default lambda parameters (C# 12+)
var increment = (int x, int by = 1) => x + by;
// With LINQ
int[] numbers = [1, 2, 3, 4, 5];
var evenNumbers = numbers.Where(n => n % 2 == 0);
String Interpolation and Raw Strings
123456789101112string name = "Alice";
int score = 95;
// Interpolated string
string msg = $"{name} scored {score}";
// Raw string literal (C# 11+) — preserves whitespace, no escaping
string json = $$"""
{
"name": "{{name}}",
"score": {{score}}
}
""";
// UTF-8 string literal (C# 11+)
ReadOnlySpan<byte> utf8 = "Hello"u8;
Object-Oriented Programming
Classes and Objects
123456789101112131415161718192021// Primary constructor (C# 12+) — works on any class/struct
public class Person(string name, int age)
{
public string Name { get; } = name;
public int Age { get; set; } = age;
public void Introduce() => Console.WriteLine($"Hi, I'm {Name}");
}
// Object creation
Person person = new("Alice", 30);
// Required members + object initializer (C# 11+)
public class User
{
public required string Email { get; init; }
public string? DisplayName { get; init; }
}
var user = new User { Email = "a@b.com" };
Records
1234567// Immutable record with value-based equality
public record Product(string Sku, decimal Price);
var p1 = new Product("ABC", 10m);
var p2 = p1 with { Price = 12m }; // non-destructive mutation
bool equal = p1 == new Product("ABC", 10m); // true
The field Keyword (C# 14)
1234567// Access the auto-generated backing field directly in property accessors
public string Name
{
get => field ?? "(unknown)";
set => field = value?.Trim();
}
Inheritance
12345678910111213// Base Class
class Animal
{
public string Name { get; set; } = "";
public virtual void MakeSound() => Console.WriteLine("Some sound");
}
// Derived Class
class Dog : Animal
{
public override void MakeSound() => Console.WriteLine("Woof!");
}
Access Modifiers
12345678910class Example
{
public int PublicField; // Accessible everywhere
private int _privateField; // Accessible only within class
protected int ProtectedField; // Accessible in class and derived classes
internal int InternalField; // Accessible within same assembly
private protected int Hybrid; // Same assembly AND derived class
file class FileLocal { } // Accessible only within this file (C# 11+)
}
Extension Members (C# 14)
12345678910// Group extension members (methods, properties) for a target type
public static class Enumerations
{
extension(IEnumerable<int> source)
{
public int SumSquares() => source.Sum(x => x * x);
public bool IsEmpty => !source.Any();
}
}
Error Handling
Exception Handling
12345678910111213141516171819202122232425try
{
int result = 10 / divisor;
}
catch (DivideByZeroException)
{
Console.WriteLine("Cannot divide by zero");
}
catch (Exception ex) when (ex.Data.Contains("Retryable"))
{
// Exception filters
}
finally
{
// Always executed
}
// Argument validation helpers (.NET 6+)
ArgumentNullException.ThrowIfNull(input);
ArgumentException.ThrowIfNullOrWhiteSpace(name);
ArgumentOutOfRangeException.ThrowIfNegative(count);
// Throwing custom exceptions
throw new ArgumentException("Invalid argument", nameof(arg));
File I/O
Reading and Writing Files
1234567891011121314using System.IO;
// Synchronous helpers
File.WriteAllText("example.txt", "Hello, World!");
string content = File.ReadAllText("example.txt");
// Async APIs (preferred for I/O)
await File.WriteAllTextAsync("example.txt", "Hello");
string text = await File.ReadAllTextAsync("example.txt");
// Stream-based with using declaration (C# 8+)
using var writer = new StreamWriter("file.txt");
await writer.WriteLineAsync("Some content");
LINQ (Language Integrated Query)
Query and Method Syntax
12345678910111213141516int[] numbers = [1, 2, 3, 4, 5];
// Query syntax
var evenNumbers = from num in numbers
where num % 2 == 0
select num;
// Method syntax
var evenNumbersMethod = numbers.Where(n => n % 2 == 0);
// Newer LINQ operators (.NET 6/7/9)
var chunks = numbers.Chunk(2); // .NET 6+
var maxBy = people.MaxBy(p => p.Age); // .NET 6+
var distinct = people.DistinctBy(p => p.Email); // .NET 6+
var indexed = numbers.Index(); // .NET 9+ — (index, item) tuples
Asynchronous Programming
Async/Await
123456789101112131415161718192021// Prefer IHttpClientFactory or a single shared HttpClient instance
private static readonly HttpClient _client = new();
public async Task<string> FetchDataAsync(CancellationToken ct = default)
{
return await _client.GetStringAsync("https://api.example.com/data", ct);
}
// Async streams (IAsyncEnumerable<T>)
public async IAsyncEnumerable<int> CountAsync([EnumeratorCancellation] CancellationToken ct = default)
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(100, ct);
yield return i;
}
}
// ValueTask for hot paths that often complete synchronously
public ValueTask<int> GetCachedAsync(string key) => /* ... */;
Collections
.NET Collections
123456789101112131415161718192021// Collection expressions (C# 12+) work for arrays, List<T>, Span<T>, etc.
int[] arr = [1, 2, 3];
List<string> names = ["Alice", "Bob", "Charlie"];
Span<int> span = [1, 2, 3];
// Spread element
int[] combined = [..arr, 4, 5];
// Dictionary
Dictionary<string, int> ages = new()
{
["Alice"] = 30,
["Bob"] = 25
};
// HashSet
HashSet<int> uniqueNumbers = [1, 2, 3];
// Immutable and frozen collections
var frozen = ages.ToFrozenDictionary(); // .NET 8+ — read-optimized
Generics
Generic Methods and Classes
123456789101112131415161718192021public class GenericClass<T>
{
private T? _value;
public void SetValue(T value) => _value = value;
}
// Generic method with constraint
public T FindMax<T>(List<T> list) where T : IComparable<T>
{
return list.Max()!;
}
// Generic math (C# 11+) using static abstract interface members
public static T Sum<T>(IEnumerable<T> values) where T : INumber<T>
{
T total = T.Zero;
foreach (var v in values) total += v;
return total;
}
Package Management
NuGet Package Management
- Add packages with
dotnet add package <PackageName> - Manage versions centrally via
Directory.Packages.props(Central Package Management) - Common packages:
- System.Text.Json — built-in, high-performance JSON (preferred over Newtonsoft.Json for new code)
- Newtonsoft.Json — still widely used where its flexibility is required
- Serilog / Microsoft.Extensions.Logging — structured logging
- AutoMapper / Mapperly — object-to-object mapping
- Entity Framework Core — ORM
- MediatR — in-process messaging
- Polly — resilience and transient-fault handling
Testing
Unit Testing Frameworks
- xUnit (most common in modern .NET projects)
- NUnit
- MSTest
- Assertion libraries: FluentAssertions, Shouldly
Example xUnit Test
12345678910111213141516171819using Xunit;
public class CalculatorTests
{
[Fact]
public void Add_PositiveNumbers_ReturnsCorrectSum()
{
var calc = new Calculator();
int result = calc.Add(2, 3);
Assert.Equal(5, result);
}
[Theory]
[InlineData(1, 2, 3)]
[InlineData(-1, 1, 0)]
public void Add_VariousInputs(int a, int b, int expected)
=> Assert.Equal(expected, new Calculator().Add(a, b));
}
Best Practices
Code Style
- Enable nullable reference types and treat warnings as errors
- Use meaningful variable and method names
- Keep methods small and focused
- Follow SOLID principles
- Use null-conditional (
?.), null-coalescing (??,??=) and pattern matching - Prefer immutability (
record,init,readonly) when possible - Use file-scoped namespaces and
global usingdirectives to reduce noise - Use
requiredmembers instead of constructors with many parameters when appropriate
Performance Tips
- Use
StringBuilderfor heavy string concatenation; otherwise interpolation is fine - Use
Span<T>,ReadOnlySpan<T>, andMemory<T>to avoid allocations - Use
async/awaitfor I/O-bound operations; pass and respectCancellationToken - Prefer
ValueTask<T>only in hot paths that frequently complete synchronously - Use
System.Text.Jsonsource generators for AOT-friendly, allocation-light serialization - Use frozen/immutable collections for read-mostly lookup tables
- Minimize boxing; use generic math interfaces (
INumber<T>) instead ofobject - Consider NativeAOT publishing for fast startup and small footprint
Resources for Further Learning
- Microsoft Learn — C# and .NET documentation (learn.microsoft.com/dotnet)
- "C# in Depth" by Jon Skeet
- "Pro C# 10 / 12" by Andrew Troelsen and Phil Japikse
- The .NET and Roslyn GitHub repositories (github.com/dotnet)
- .NET Blog and "What's new in C# 14" docs
- Stack Overflow C# community
Conclusion
C# continues to evolve rapidly alongside .NET, gaining features like primary constructors, collection expressions, the field keyword, and extension members while maintaining strong typing and a rich ecosystem. Staying current with the latest language version and runtime (now C# 14 on .NET 10 LTS) is the best way to write expressive, performant, and maintainable code.
Continue Learning
Discover more cheatsheets to boost your productivity