C# is statically typed. Every variable has a type known at compile time, which catches errors early and enables tooling like IntelliSense.

Basic Types

  int age = 30;
double price = 19.99;
decimal salary = 75000.50m;  // precise financial calculations
bool isActive = true;
char grade = 'A';
string name = "Alice";

Console.WriteLine($"{name} is {age}, active: {isActive}");
  

var and Type Inference

  var count = 42;           // inferred as int
var message = "Hello";    // inferred as string
var items = new List<int> { 1, 2, 3 };

// var list = null;       // error: cannot infer from null
  

Use var when the type is obvious from the right-hand side.

Value vs Reference Types

  // Value type — copied on assignment
int a = 10;
int b = a;
b = 20;
Console.WriteLine(a);  // 10

// Reference type — shares memory
int[] arr1 = { 1, 2, 3 };
int[] arr2 = arr1;
arr2[0] = 99;
Console.WriteLine(arr1[0]);  // 99
  

Structs are value types; classes, arrays, and strings are reference types.

Type Conversion

  // Implicit (safe widening)
int i = 10;
double d = i;

// Explicit (narrowing — may lose data)
double pi = 3.14;
int truncated = (int)pi;  // 3

// Parse strings
int parsed = int.Parse("42");
bool ok = int.TryParse("abc", out int result);  // false
  

Nullable Value Types

  int? nullableInt = null;
nullableInt = 5;

if (nullableInt.HasValue)
{
    Console.WriteLine(nullableInt.Value);
}

// Null-coalescing
int value = nullableInt ?? 0;
  

Nullable Reference Types

Enable in project file:

  <Nullable>enable</Nullable>
  
  string nonNull = "hello";
string? maybeNull = null;

// Compiler warns if you dereference maybeNull without checking
Console.WriteLine(maybeNull?.Length ?? 0);
  

This feature helps prevent null reference exceptions at compile time.

string vs StringBuilder

  // Concatenation in a loop — slow (creates many string objects)
string result = "";
for (int i = 0; i < 1000; i++)
    result += i;

// StringBuilder — efficient for many mutations
var sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
    sb.Append(i);
string built = sb.ToString();
  

Records (C# 9+)

  public record Point(int X, int Y);
public record Person(string Name, int Age);

var p1 = new Point(1, 2);
var p2 = p1 with { X = 10 };  // non-destructive mutation
Console.WriteLine(p1 == p2);  // false — value equality
  

Records provide built-in equality, ToString(), and with expressions.

Pattern Matching on Types

  object obj = "hello";
string message = obj switch
{
    int n => $"Number: {n}",
    string s => $"String: {s}",
    _ => "Unknown"
};
  

Constants vs readonly

  public const int MaxRetries = 3;           // compile-time constant
public static readonly DateTime AppStart = DateTime.UtcNow;  // runtime
  

Use const for true constants; readonly for values set once at runtime.

Common Pitfalls

  • Floating-point equality: 0.1 + 0.2 != 0.3 — use decimal for money.
  • Boxing value types into object causes allocations — prefer generics.
  • Nullable reference warnings ignored — fix them instead of suppressing globally.