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:

</> Program.cs
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);
Output
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:

</> Program.cs
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}");
Output
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:

</> Program.cs
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));
Output
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.

A crack already showing: nothing here stops other code from writing 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.