C# logoC#v14INTERMEDIATE

C# Programming Language

C# cheat sheet with syntax, LINQ, async/await, classes, generics, pattern matching, and .NET best practices with code examples.

45 min read

Sign in to mark items as known and track your progress.

Sign in

Getting Started

Basic setup and first program

Hello World

Your first C# program

csharp
// Simple console output
Console.WriteLine("Hello, World!");
Console.Write("Same line");
Console.Write(" output");

// Reading input
Console.Write("Enter your name: ");
string name = Console.ReadLine();
Console.WriteLine($"Hello, {name}!");
💡 Console.WriteLine adds a new line, Console.Write doesn't
⚡ Top-level programs (C# 9+) remove boilerplate
📌 $ before strings enables string interpolation
🟢 Console.ReadLine() always returns a string

Comments

Code documentation

csharp
// Single line comment

/* Multi-line
   comment block */

/// <summary>
/// XML documentation comment
/// </summary>

// TODO: Remember to implement this
// HACK: Temporary fix - refactor later
// NOTE: Important information
💡 Use /// for IntelliSense documentation
⚡ Regions help organize large files
📌 TODO comments are tracked by Visual Studio
🟢 XML comments generate documentation files

Variables & Data Types

Basic data types and variable declarations

Basic Types

Common data types

csharp
// Numbers
int whole = 42;              // Whole numbers (-2,147,483,648 to 2,147,483,647)
double pi = 3.14;       // Decimals (15-16 digits precision)
float smaller = 3.14f;       // Smaller decimals (6-7 digits)
decimal money = 19.99m;      // Financial calculations (28-29 digits)
long bigNumber = 9000000000L; // Large whole numbers

// Text
string text = "Hello";
char letter = 'A';

// Boolean
bool isTrue = true;
bool isFalse = false;
💡 Use decimal for money to avoid rounding errors
⚡ int is the most common integer type
📌 Add f for float, m for decimal, L for long
🟢 string is for text, char is for single characters

Variable Declaration

Declaring and initializing variables

csharp
// Declaration and initialization
int age = 25;
string name = "John";

// Declaration then assignment
int score;
score = 100;

// Multiple declarations
int x = 1, y = 2, z = 3;

// Type inference with var
var message = "Hello";  // Compiler infers string
var number = 42;        // Compiler infers int
var price = 19.99;      // Compiler infers double

// Constants (cannot change)
const double PI = 3.14159;
const int MAX_SIZE = 100;
💡 Use var when type is obvious from right side
⚡ const for compile-time constants, readonly for runtime
📌 Variables are case-sensitive (age ≠ Age)
🟢 Follow C# naming conventions: camelCase for variables

Control Flow

Conditionals and loops

If Statements

Conditional execution

csharp
// Basic if
if (age >= 18)
{
    Console.WriteLine("Adult");
}

// If-else
if (score >= 90)
{
    Console.WriteLine("A");
}
else if (score >= 80)
{
    Console.WriteLine("B");
}
else
{
    Console.WriteLine("C or below");
}

// Ternary operator
string status = age >= 18 ? "Adult" : "Minor";

// Null checking
if (name != null)
{
    Console.WriteLine(name.Length);
}
💡 Use && for AND, || for OR, ! for NOT
⚡ Ternary operator (? :) for simple if-else
📌 Always use braces {} even for single statements
🟢 Pattern matching makes type checking cleaner

Loops

Iteration and repetition

csharp
// For loop
for (int i = 0; i < 5; i++)
{
    Console.WriteLine(i);
}

// While loop
int count = 0;
while (count < 5)
{
    Console.WriteLine(count);
    count++;
}

// Do-while (runs at least once)
do
{
    Console.WriteLine("Run at least once");
} while (false);

// Foreach loop
int[] numbers = { 1, 2, 3, 4, 5 };
foreach (int num in numbers)
{
    Console.WriteLine(num);
}
💡 Use foreach when you don't need the index
⚡ break exits loop, continue skips to next iteration
📌 While for unknown iterations, for when count is known
🟢 Do-while guarantees at least one execution

Methods (Functions)

Defining and calling methods

Method Basics

Creating and using methods

csharp
// Simple method
void SayHello()
{
    Console.WriteLine("Hello!");
}

// Method with parameters
void Greet(string name)
{
    Console.WriteLine($"Hello, {name}!");
}

// Method with return value
int Add(int a, int b)
{
    return a + b;
}

// Calling methods
SayHello();
Greet("Alice");
int sum = Add(5, 3);
💡 void means method doesn't return a value
⚡ Static methods belong to class, instance methods to objects
📌 Method overloading allows same name with different parameters
🟢 Use => for single-expression methods

Parameters & Returns

Advanced parameter passing

csharp
// Pass by value (default)
void ChangeValue(int x)
{
    x = 100;  // Doesn't affect original
}

// Pass by reference
void ChangeRef(ref int x)
{
    x = 100;  // Changes original
}

// Out parameters (must assign)
bool TryParse(string input, out int result)
{
    return int.TryParse(input, out result);
}

// Multiple returns with tuples
(int sum, int product) Calculate(int a, int b)
{
    return (a + b, a * b);
}

// Named arguments
PrintInfo(age: 25, name: "Bob");
💡 ref modifies original, out returns multiple values
⚡ params allows variable number of arguments
📌 in parameters for read-only reference passing
🟢 Tuples are great for returning multiple values

Object-Oriented Programming

Classes, objects, inheritance, and polymorphism

Classes and Objects

Defining classes and creating objects

csharp
// Simple class
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void Introduce()
    {
        Console.WriteLine($"Hi, I'm {Name}");
    }
}

// Usage
Person person = new Person();
person.Name = "Alice";
person.Age = 30;
person.Introduce();
💡 Auto-properties provide shorthand for simple get/set operations
⚡ Constructor initializes object state when created
📌 Access modifiers control visibility (public, private, protected)
🟢 Object initializer syntax allows setting properties during creation

Inheritance and Polymorphism

Class inheritance and method overriding

csharp
// Basic inheritance
public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("Some sound");
    }
}

public class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Woof!");
    }
}

// Usage
Animal myPet = new Dog();
myPet.MakeSound(); // "Woof!"
💡 Abstract classes cannot be instantiated directly
⚡ Virtual methods can be overridden in derived classes
📌 base keyword calls parent class constructor or methods
🟢 Polymorphism allows treating derived objects as base type

Collections and Generics

Working with collections and generic types

Generic Collections

List, Dictionary, and other generic collections

csharp
// List<T>
List<string> names = new List<string> { "Alice", "Bob" };
names.Add("Charlie");

// Dictionary<TKey, TValue>
Dictionary<string, int> ages = new Dictionary<string, int>();
ages["Alice"] = 30;
ages["Bob"] = 25;

// Array
int[] numbers = { 1, 2, 3, 4, 5 };

// HashSet<T>
HashSet<int> uniqueNumbers = new HashSet<int> { 1, 2, 3 };
💡 Generic collections provide type safety and performance
⚡ Dictionary provides O(1) average lookup time
📌 HashSet automatically handles uniqueness of elements
🟢 Queue (FIFO) and Stack (LIFO) for specific ordering needs

LINQ and Lambda Expressions

Language Integrated Query and functional programming

LINQ Query Syntax

Querying collections with LINQ

csharp
// Query syntax
var adults = from p in people
             where p.Age >= 18
             select p;

// Method syntax
var adults2 = people.Where(p => p.Age >= 18);

// Common operations
var names = people.Select(p => p.Name);
var sorted = people.OrderBy(p => p.Age);
var first = people.First();
💡 LINQ provides SQL-like querying for .NET collections
⚡ Method syntax often more flexible than query syntax
📌 Lambda expressions (=>) create anonymous functions
🟢 LINQ is lazy-evaluated until enumerated

Async/Await and Tasks

Asynchronous programming patterns

Basic Async/Await

Asynchronous methods and Task handling

csharp
// Async method
public async Task<string> GetDataAsync()
{
    await Task.Delay(1000);
    return "Data";
}

// Using async method
string result = await GetDataAsync();

// Multiple async operations
Task<string> task1 = GetDataAsync();
Task<string> task2 = GetDataAsync();
string[] results = await Task.WhenAll(task1, task2);
💡 async/await makes asynchronous code look synchronous
⚡ Use Task.WhenAll for concurrent operations
📌 Avoid async void except for event handlers
🟢 ConfigureAwait(false) can improve performance in libraries

Exception Handling

Error handling and custom exceptions

Try-Catch-Finally

Basic exception handling patterns

csharp
// Basic try-catch
try
{
    int result = 10 / 0;
}
catch (DivideByZeroException ex)
{
    Console.WriteLine($"Error: {ex.Message}");
}
finally
{
    Console.WriteLine("Cleanup");
}
💡 Catch specific exceptions before general ones
⚡ using statement ensures proper resource disposal
📌 finally block always executes, even after return
🟢 Exception filters allow conditional catch blocks

Interfaces and Abstract Classes

Contracts and abstract implementations

Interface Implementation

Creating and implementing interfaces

csharp
// Simple interface
public interface IPlayable
{
    void Play();
    void Pause();
}

public class Video : IPlayable
{
    public void Play() => Console.WriteLine("Playing video");
    public void Pause() => Console.WriteLine("Pausing video");
}
💡 Interfaces define contracts without implementation
⚡ C# 8+ allows default interface methods
📌 Classes can implement multiple interfaces
🟢 Use interfaces for dependency injection and testing

Delegates and Events

Function pointers and event-driven programming

Delegates and Func/Action

Working with delegates and built-in delegate types

csharp
// Delegate declaration
public delegate void MyDelegate(string message);

// Using delegates
MyDelegate handler = Method1;
handler += Method2; // Multicast
handler("Hello");

// Built-in delegates
Action<string> logger = Console.WriteLine;
Func<int, int, int> add = (x, y) => x + y;
Predicate<int> isEven = n => n % 2 == 0;
💡 Delegates are type-safe function pointers
⚡ Action for void methods, Func for methods with return values
📌 Multicast delegates can call multiple methods
🟢 Events are special multicast delegates with restrictions

Properties and Indexers

Advanced property patterns and indexers

Property Patterns

Auto-properties, computed properties, and validation

csharp
// Auto-properties
public string Name { get; set; }
public int Age { get; private set; }

// Read-only property
public string Id { get; } = Guid.NewGuid().ToString();

// Computed property
public string FullName => $"{FirstName} {LastName}";

// Property with validation
private int _age;
public int Age
{
    get => _age;
    set => _age = value >= 0 ? value : 0;
}
💡 Init-only properties can only be set during object initialization
⚡ Computed properties recalculate on each access
📌 Property setters can include validation logic
🟢 Indexers allow array-like access to custom classes

Extension Methods and Nullable Types

Extending existing types and handling null values

Extension Methods

Adding methods to existing types

csharp
// Extension method
public static class StringExtensions
{
    public static bool IsValidEmail(this string email)
    {
        return email?.Contains("@") ?? false;
    }
}

// Usage
string email = "test@example.com";
bool valid = email.IsValidEmail();

// Nullable types
int? nullableInt = null;
string? nullableString = null;

// Null operators
int value = nullableInt ?? 0; // Null coalescing
string length = nullableString?.Length.ToString() ?? "0";
💡 Extension methods must be in static classes with static methods
⚡ First parameter with this keyword defines extended type
📌 Null-conditional operator (?.) safely accesses members
🟢 Nullable reference types help catch null issues at compile time

Attributes and Reflection

Metadata and runtime type inspection

Custom Attributes

Using and creating attributes for metadata

csharp
// Built-in attributes
[Obsolete("Use NewMethod instead")]
public void OldMethod() { }

[Serializable]
public class MyClass { }

// Custom attribute
[AttributeUsage(AttributeTargets.Property)]
public class RequiredAttribute : Attribute { }

// Using custom attribute
public class User
{
    [Required]
    public string Name { get; set; }
}
💡 Attributes add metadata to code elements
⚡ Reflection allows runtime type inspection and manipulation
📌 Custom attributes enable declarative programming
🟢 Use reflection sparingly as it impacts performance

Pattern Matching

Advanced pattern matching and switch expressions

Switch Expressions

Modern pattern matching with switch expressions

csharp
// Switch expression
string result = number switch
{
    1 => "One",
    2 => "Two",
    < 0 => "Negative",
    > 100 => "Large",
    _ => "Other"
};

// Property patterns
string description = person switch
{
    { Age: < 18 } => "Minor",
    { Age: >= 65 } => "Senior",
    _ => "Adult"
};

// Type patterns
object obj = GetValue();
string type = obj switch
{
    int i => $"Integer: {i}",
    string s => $"String: {s}",
    null => "Null",
    _ => "Unknown"
};
💡 Switch expressions provide concise pattern matching
⚡ Property patterns match on object properties
📌 Guard clauses (when) add additional conditions
🟢 List patterns enable array/collection matching

Records and Value Types

Modern data types and value semantics

Record Types

Immutable reference types with value semantics

csharp
// Record declaration
public record Person(string Name, int Age);

// Using records
var person = new Person("Alice", 30);
var older = person with { Age = 31 };

// Struct
public struct Point
{
    public double X { get; init; }
    public double Y { get; init; }
}

// Value equality
var p1 = new Person("Bob", 25);
var p2 = new Person("Bob", 25);
bool equal = p1 == p2; // true
💡 Records provide value equality and immutability by default
⚡ with expressions create copies with modified properties
📌 Records automatically implement ToString, Equals, GetHashCode
🟢 Use readonly structs for better performance

Memory Management

Memory optimization and performance considerations

IDisposable and Using

Resource management and deterministic cleanup

csharp
// Using statement
using var file = File.OpenRead("data.txt");
// Automatically disposed at end of scope

// IDisposable pattern
public class Resource : IDisposable
{
    public void Dispose()
    {
        // Cleanup
        GC.SuppressFinalize(this);
    }
}

// Span<T> for efficient memory
Span<int> numbers = stackalloc int[100];

// Memory<T> for async scenarios
Memory<byte> buffer = new byte[1024];
💡 IDisposable pattern ensures deterministic resource cleanup
⚡ Span<T> enables zero-allocation array operations
📌 ArrayPool reduces garbage collection pressure
🟢 Using declarations simplify resource management

Latest C# Features

Modern C# features and syntax improvements

C# 10-12 Features

Latest language enhancements and syntax improvements

csharp
// Global using (C# 10)
global using System;
global using System.Collections.Generic;

// File-scoped namespace (C# 10)
namespace MyApp.Services;

// Raw string literals (C# 11)
string json = """
{
    "name": "John",
    "age": 30
}
""";

// Required members (C# 11)
public class Person
{
    public required string Name { get; init; }
}

// Collection expressions (C# 12)
int[] numbers = [1, 2, 3, 4, 5];
💡 Global using reduces repetitive using statements
⚡ Raw string literals handle multi-line strings elegantly
📌 Required members ensure proper object initialization
🟢 Collection expressions provide concise initialization