A class is the primary way C# bundles data and behavior. This chapter
walks through every member a class can contain and describes how records and interfaces
complement the class model.
5.1 Class Anatomy
A class definition may include any combination of these member kinds:
Fields — per-instance storage (keep private or protected)
Properties — controlled access to data via get/set accessors
Constructors — initialize the object on creation
Methods — behavior
Events — delegate-typed notification hooks
Nested types — helper types scoped to the class
Static members — shared by all instances (or the type itself)
Finalizer — ~ClassName(), called by the GC before reclaim
public class BankAccount {
private decimal _balance; // field
public string Owner { get; } // auto property (get-only)
public decimal Balance => _balance; // computed property
public BankAccount(string owner, decimal initial) { // constructor
Owner = owner;
_balance = initial;
}
public void Deposit(decimal amount) { // method
if (amount <= 0) throw new ArgumentException("amount");
_balance += amount;
}
public bool Withdraw(decimal amount) {
if (amount > _balance) return false;
_balance -= amount;
return true;
}
public override string ToString() =>
$"{Owner}: {_balance:C}";
}
5.2 Access Modifiers
Modifier
Accessible from
public
anywhere
private
same class only (default for members)
protected
same class + derived classes
internal
same assembly (default for top-level types)
protected internal
same assembly or derived class
private protected
same class or derived class in same assembly
5.3 Properties
Properties look like fields to callers but execute code. They are the idiomatic way to
expose data in C#:
public class Person {
private string _name = "";
// full property with validation
public string Name {
get => _name;
set => _name = value?.Trim() ?? throw new ArgumentNullException();
}
// auto property with init-only setter (C# 9+)
public int Age { get; init; }
// computed property (no backing field)
public bool IsAdult => Age >= 18;
}
Object initializers work with settable or init-only properties:
var p = new Person { Name = "Alice", Age = 30 };
5.4 Constructors
Constructors initialize instances. You may define multiple overloads.
this(...) chains to another constructor in the same class;
base(...) chains to the base class constructor.
public class Point {
public double X { get; }
public double Y { get; }
public Point() : this(0, 0) { } // calls below
public Point(double x, double y) {
X = x; Y = y;
}
}
Primary constructors (C# 12) let you declare parameters directly on the
class declaration, keeping frequently injected dependencies concise:
class Logger(IOutput output) { /* output is in scope */ }Static constructors run once before any instance is created or static
member is accessed. They take no arguments and cannot be called explicitly.
5.5 Static Members and Classes
Static members belong to the type itself. A static class contains only
static members, cannot be instantiated, and is sealed. It is the natural home for
utility methods and extension methods:
public static class MathUtils {
public static double DegreesToRadians(double deg) => deg * Math.PI / 180;
public static double Clamp(double v, double lo, double hi) =>
Math.Max(lo, Math.Min(hi, v));
}
5.6 Records
A record (C# 9+) is a class (or struct) with compiler-generated
value-equality, a readable ToString(), and a
non-destructive copy via with:
record Point(double X, double Y); // positional record
var p1 = new Point(1, 2);
var p2 = p1 with { Y = 5 }; // p2 = Point(1, 5)
Console.WriteLine(p1 == new Point(1, 2)); // True — value equality
Records are ideal for immutable data transfer objects, domain value objects, and
pattern-matching targets. A record struct gives the same semantics on a
value type.
5.7 Interfaces
An interface defines a contract: a set of members a type must implement.
A class or struct may implement any number of interfaces.
public interface IShape {
double Area { get; }
double Perimeter { get; }
string Describe() => $"Area={Area:F2}, Perim={Perimeter:F2}"; // default impl C# 8+
}
public class Circle : IShape {
public double Radius { get; }
public Circle(double r) => Radius = r;
public double Area => Math.PI * Radius * Radius;
public double Perimeter => 2 * Math.PI * Radius;
}
Interfaces support default implementations (C# 8+), static abstract
members (C# 11), and generic constraints (
where T : IShape).
5.8 IDisposable and Finalizers
Classes that hold unmanaged resources (file handles, network connections) should implement
IDisposable and follow the dispose pattern so callers can use the
using statement for deterministic cleanup:
public class ResourceHolder : IDisposable {
private bool _disposed;
private readonly FileStream _stream;
public ResourceHolder(string path) =>
_stream = File.OpenRead(path);
public void Dispose() {
if (!_disposed) {
_stream.Dispose();
_disposed = true;
}
GC.SuppressFinalize(this);
}
~ResourceHolder() => Dispose(); // safety net finalizer
}
5.9 Epilogue
This chapter detailed the anatomy of C# classes: fields, properties, constructors, methods,
access control, static members, records, and interfaces. The next chapter examines how
classes relate to one another through inheritance and composition.