Classes & Objects
Everything so far has dealt with loose variables and standalone methods. A class lets you bundle related data and behavior together into one unit — a blueprint. An object is a specific thing built from that blueprint, with its own independent copy of the data.
Defining a class
Think of a bank account: it has data (who owns it, how much money is in it) and behavior (depositing money changes the balance and probably prints something). A class groups exactly that:
class BankAccount
{
public string Owner;
public double Balance;
public void Deposit(double amount)
{
Balance += amount;
Console.WriteLine($"Deposited {amount}. New balance: {Balance}");
}
}
BankAccount account = new BankAccount();
account.Owner = "Jamie";
account.Balance = 100;
account.Deposit(50);
Deposited 50. New balance: 150
Owner and Balance are fields — the data each account carries. Deposit is a method that belongs to the class, and inside it, Balance refers to that particular account's balance without needing any extra qualification.
One blueprint, many objects
The whole point of a class is that new BankAccount() can be called as many times as you like, and each call produces a completely independent object. Changing one account's balance never touches another's:
BankAccount account1 = new BankAccount(); account1.Owner = "Jamie"; account1.Balance = 100; BankAccount account2 = new BankAccount(); account2.Owner = "Sasha"; account2.Balance = 500; account1.Deposit(25); Console.WriteLine($"{account1.Owner}: {account1.Balance}"); Console.WriteLine($"{account2.Owner}: {account2.Balance}");
Deposited 25. New balance: 125 Jamie: 125 Sasha: 500
Depositing into account1 left account2's balance of 500 completely undisturbed — they're separate objects in memory, even though they came from the same class.
Methods that return a result
A withdrawal needs to fail gracefully when there isn't enough money — a good fit for a method that returns a bool telling the caller whether it worked:
class BankAccount
{
public string Owner;
public double Balance;
public bool Withdraw(double amount)
{
if (amount > Balance)
{
Console.WriteLine("Insufficient funds.");
return false;
}
Balance -= amount;
return true;
}
}
BankAccount account = new BankAccount();
account.Owner = "Jamie";
account.Balance = 100;
Console.WriteLine(account.Withdraw(30));
Console.WriteLine(account.Balance);
Console.WriteLine(account.Withdraw(200));
True 70 Insufficient funds. False
The first withdrawal succeeds, drops the balance to 70, and returns true. The second tries to take out more than what's left, prints a message, and returns false without touching the balance at all.
account.Balance = -9999; directly, bypassing Withdraw entirely — Balance is just a public field anyone can poke. The next lesson, on properties, fixes exactly this problem.