Lesson 88 min read

Classes & Objects

Build your own custom types — the blueprint pattern

Classes Are Blueprints

A class is like a blueprint for a house. The blueprint itself isn't a house — it's a plan that describes what a house looks like (rooms, doors, windows). When you actually build a house from that blueprint, that's an object (also called an "instance").

You can build many houses from the same blueprint. Each house is its own thing — paint one red, another blue — but they all share the same structure.

Classes are the building blocks of every data structure, from linked lists to trees. A class bundles together:

  • Fields/Properties — the data (what the object knows)
  • Methods — the behavior (what the object can do)
  • Constructor — the setup instructions (how to build it)

Your First Class

// The blueprint
class Dog
{
// Properties — what a Dog knows about itself
public string Name { get; set; }
public string Breed { get; set; }
public int Age { get; set; }
// Constructor — how to build a Dog
public Dog(string name, string breed, int age)
{
Name = name;
Breed = breed;
Age = age;
}
// Method — what a Dog can do
public string Bark()
{
return Age < 2 ? "Yip! Yip!" : "Woof! Woof!";
}
public string Introduce()
{
return $"I'm {Name}, a {Age}-year-old {Breed}.";
}
}
// Create objects from the blueprint
var buddy = new Dog("Buddy", "Golden Retriever", 5);
var puppy = new Dog("Tiny", "Chihuahua", 1);
Console.WriteLine(buddy.Introduce());
Console.WriteLine($"Buddy says: {buddy.Bark()}");
Console.WriteLine(puppy.Introduce());
Console.WriteLine($"Tiny says: {puppy.Bark()}");
Output
I'm Buddy, a 5-year-old Golden Retriever.
Buddy says: Woof! Woof!
I'm Tiny, a 1-year-old Chihuahua.
Tiny says: Yip! Yip!

Properties — Smart Fields

Properties look like fields but have superpowers. The { get; set; } syntax creates an auto-property — C# generates a hidden backing field automatically. But you can also write custom logic:

  • get — runs when someone reads the value
  • set — runs when someone assigns a value (use value keyword)
  • init — like set, but only works during object creation

Properties let you add validation — like a bouncer checking IDs at the door.

Properties & Access Modifiers

class BankAccount
{
public string Owner { get; init; } // set only during creation
private decimal _balance; // private backing field
public decimal Balance
{
get => _balance;
private set // only this class can set it
{
if (value < 0)
throw new ArgumentException("Balance can't be negative!");
_balance = value;
}
}
// Computed property — no backing field needed
public string Status => Balance > 1000 ? "Gold Member" : "Standard";
public BankAccount(string owner, decimal initialDeposit)
{
Owner = owner;
Balance = initialDeposit;
}
public void Deposit(decimal amount)
{
Balance += amount;
Console.WriteLine($"Deposited {amount:C}. New balance: {Balance:C}");
}
public void Withdraw(decimal amount)
{
if (amount > Balance)
{
Console.WriteLine("Insufficient funds!");
return;
}
Balance -= amount;
Console.WriteLine($"Withdrew {amount:C}. New balance: {Balance:C}");
}
}
var account = new BankAccount("Luna", 500);
Console.WriteLine($"Owner: {account.Owner}, Status: {account.Status}");
account.Deposit(700);
Console.WriteLine($"Status: {account.Status}");
account.Withdraw(200);
account.Withdraw(5000);
Output
Owner: Luna, Status: Standard
Deposited $700.00. New balance: $1,200.00
Status: Gold Member
Withdrew $200.00. New balance: $1,000.00
Insufficient funds!

Static Members — Shared Across All Objects

Normal properties and methods belong to each individual object. static members belong to the class itself — they're shared by everyone.

Think of it this way: Every student has their own name (instance property), but they all share the same school name (static property). You access static members through the class name, not through an object.

Static Members

class Player
{
// Static — shared by ALL players
public static int TotalPlayers { get; private set; } = 0;
// Instance — unique to each player
public string Name { get; set; }
public int Score { get; set; }
public Player(string name)
{
Name = name;
Score = 0;
TotalPlayers++; // every new player increments the count
}
// Static method
public static void ShowPlayerCount()
{
Console.WriteLine($"Total players in the game: {TotalPlayers}");
}
public override string ToString() => $"{Name} (Score: {Score})";
}
var p1 = new Player("Alice");
var p2 = new Player("Bob");
var p3 = new Player("Charlie");
p1.Score = 100;
p2.Score = 85;
Console.WriteLine(p1);
Console.WriteLine(p2);
Player.ShowPlayerCount(); // called on the CLASS, not an object
Output
Alice (Score: 100)
Bob (Score: 85)
Total players in the game: 3
Note: 🔒 Access modifiers cheat sheet: public = anyone can access. private = only this class. protected = this class + its children. internal = only within this project. Start with private and only open up access when you need to. It's easier to make things more public later than to lock them down.

Quick check

What is the difference between a class and an object?
MethodsInheritance & Polymorphism