C# Classes and Objects

Model real things with classes, create objects with new, give them fields and methods, protect their data with private and public, and understand reference types, null and static members.

Lesson 11 of 43Intermediate17 min read

So far your data has lived in separate variables: a name here, a balance there. Real programs model things: customers, orders, bank accounts, enemies in a game. Each thing has data (a customer's name and email) and behaviour (placing an order, changing an email). Object-oriented programming bundles the data and behaviour together.

A class is a blueprint that describes what data and behaviour a kind of thing has. An object is one actual thing built from that blueprint. BankAccount is a class; your account and my account are two objects, each with its own balance. C# is built around classes: everything you've used so far, from string to List<T> to Console, is defined by one.

In this lesson you'll write classes with fields and methods, create objects with new, hide internal data with access modifiers so it can only change in valid ways, and learn how objects behave when you assign them, compare them and share them.

Defining a class and creating objects

C#
var account = new BankAccount();
account.Owner = "Ada";
account.Deposit(100m);
Console.WriteLine(account.GetBalance());   // 100

class BankAccount
{
    public string Owner = "";
    private decimal balance;

    public void Deposit(decimal amount)
    {
        balance += amount;
    }

    public decimal GetBalance() => balance;
}
  • Fields are variables that belong to an object: each account has its own balance.
  • Methods inside the class describe what objects can do, and can use the object's fields.
  • new BankAccount() creates an object, also called an instance.
  • Use the dot to reach members: account.Deposit(100m).
  • In a real project each class usually lives in its own file. When you put classes in Program.cs, they must come after the top-level statements, as in the examples below.

Access modifiers and encapsulation

  • public members can be used from anywhere.
  • private members can only be used inside the class. This is the default for class members.
  • internal members are visible within the same project, and protected members are visible to subclasses.

Keeping fields private and offering public methods is called encapsulation. Because nobody outside can write account.balance = -500, every change goes through Deposit and Withdraw, where you can check the rules. In practice, C# exposes data through properties, covered in the next lesson.

this, ToString and static

  • this refers to the current object. It's needed when a parameter has the same name as a field: this.name = name;.
  • Every class inherits a ToString() method. Overriding it controls how the object prints: public override string ToString() => $"{Owner}: {balance}";.
  • static members belong to the class itself, not to any one object. A static field such as static int count is shared by every instance; static methods like Math.Max are called on the class name.

Objects are references

Classes are reference types. A variable holds a reference to the object, not the object itself:

C#
var a = new BankAccount();
var b = a;                 // b refers to the SAME account
b.Deposit(50m);
Console.WriteLine(a.GetBalance());   // 50

A reference can also be null, meaning it points to nothing. Calling a method on null throws a NullReferenceException, which is why modern C# warns you about variables that might be null.

C# Object-Oriented Programming Syntax

Class syntax

public class Counter
{
    private int value;
    public static int Created;

    public void Increment() => value++;
    public int Current() => value;
    public override string ToString() => $"Counter at {value}";
}

var c = new Counter();
c.Increment();
Console.WriteLine(c);            // calls ToString
Counter.Created++;               // static member through the class

C# Object-Oriented Programming Summary

  • A class is a blueprint; objects are instances created with new.
  • Fields hold each object's data; methods define its behaviour.
  • private hides members inside the class; public exposes them; private is the default.
  • static members belong to the class and are shared by all instances.
  • Class variables hold references; two variables can refer to the same object, or to null.

C# Object-Oriented Programming Examples

Each example shows the code, the output it produces and what each part does.

Example 1

A bank account with rules

C#
var acc = new BankAccount { Owner = "Ada" };
acc.Deposit(120m);
Console.WriteLine(acc.Withdraw(200m));
Console.WriteLine(acc.Withdraw(45.5m));
acc.Deposit(-30m);
Console.WriteLine(acc);

class BankAccount
{
    public string Owner = "";
    private decimal balance;

    public bool Withdraw(decimal amount)
    {
        if (amount <= 0 || amount > balance) return false;
        balance -= amount;
        return true;
    }

    public void Deposit(decimal amount)
    {
        if (amount > 0) balance += amount;
    }

    public override string ToString() => $"{Owner}: £{balance:F2}";
}
Output
False
True
Ada: £74.50
Line-by-line explanation
  1. Line 1An object initializer sets public members as the object is created.
  2. Line 6WriteLine calls ToString automatically: 120 minus 45.50 is 74.50.
  3. Line 8In Program.cs, classes come after the top-level statements.
  4. Line 11balance is private, so it can only change through the methods below.
  5. Line 15Invalid withdrawals are refused, so the balance can never go negative.
  6. Line 22Negative deposits are ignored.
  7. Line 25Overriding ToString controls how the object prints.
Example 2

Static members

C#
var t1 = new Ticket("Concert");
var t2 = new Ticket("Concert");
var t3 = new Ticket("Theatre");

Console.WriteLine($"{t1.Number} {t2.Number} {t3.Number}");
Console.WriteLine($"Issued: {Ticket.Issued}");

class Ticket
{
    private static int nextNumber = 1;
    public static int Issued => nextNumber - 1;

    public int Number;
    public string Event;

    public Ticket(string eventName)
    {
        Number = nextNumber++;
        Event = eventName;
    }
}
Output
1 2 3
Issued: 3
Line-by-line explanation
  1. Line 6A static member is read through the class name, not an object.
  2. Line 10One nextNumber is shared by every ticket, because it's static.
  3. Lines 16 to 20A constructor runs when each ticket is created; the next lesson covers them fully.
  4. Line 18Each ticket takes the current number, then the shared counter goes up.
Example 3

References and null

C#
var p1 = new Player { Name = "Sam", Score = 10 };
var p2 = p1;
p2.Score = 99;
Console.WriteLine(p1.Score);

var p3 = new Player { Name = "Sam", Score = 99 };
Console.WriteLine(p1 == p2);
Console.WriteLine(p1 == p3);

Player? nobody = null;
Console.WriteLine(nobody?.Name ?? "No player");

class Player
{
    public string Name = "";
    public int Score;
}
Output
99
True
False
No player
Line-by-line explanation
  1. Line 2p2 refers to the same object as p1, so changing p2 changes p1.
  2. Line 7== on classes compares references: p1 and p2 are the same object.
  3. Line 8p3 has the same data but is a different object.
  4. Lines 10 to 11?. returns null instead of throwing when the reference is null, and ?? gives a fallback.

Common C# Object-Oriented Programming Mistakes

Using an object before creating it

Don't
BankAccount acc = null!;
acc.Deposit(10);               // NullReferenceException
Do
var acc = new BankAccount();
acc.Deposit(10);

Public fields that anyone can break

Don't
public decimal Balance;        // any code can write acc.Balance = -1000
Do
private decimal balance;
public decimal GetBalance() => balance;
public void Deposit(decimal amount) { if (amount > 0) balance += amount; }

Using static for per-object data

Don't
class Player { public static int Score; }   // every player shares one score
Do
class Player { public int Score; }          // each player has their own

C# Object-Oriented Programming in Real Projects

Classes in .NET applications

An online shop's code has classes like Customer, Order, Product and PaymentService. Data classes describe what's stored, while service classes contain behaviour such as OrderService.PlaceOrder. ASP.NET Core creates service objects for you and passes them where they're needed, a technique called dependency injection. Unity games define a class for each kind of game object, such as PlayerController or Enemy.

C# Object-Oriented Programming FAQ

What is the difference between a class and an object in C#?

A class is the blueprint: it declares which fields, properties and methods its objects will have. An object is one instance made from that blueprint with new, holding its own copy of the data. BankAccount is a class; each new BankAccount() is a separate object with its own balance.

What is the difference between a field and a property in C#?

A field is a variable stored directly in the object, such as private decimal balance;. A property looks like a field from outside but runs get and set code, so the class can validate values, compute results or make data read-only. The usual convention is private fields and public properties.

Why do I get a NullReferenceException?

You called a method or read a member on a variable that holds null, so there's no object to use. Common causes are forgetting new, a method that returned null, or a field that was never assigned. Read the stack trace to find the line, and pay attention to the compiler's nullable warnings, which point out many of these cases before you run.

What does the default access modifier mean for class members?

If you don't write a modifier, class members are private, so only code inside that class can use them. A top-level class itself defaults to internal, meaning it's visible anywhere in the same project. Writing modifiers explicitly makes your intent clear to readers.

Should each class be in its own file in C#?

It's the common convention to put each public class in a file with the same name, such as BankAccount.cs, because it makes code easy to find. The compiler doesn't require it, and small related types, such as a record used only by one class, often share a file.

C# Object-Oriented Programming Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Complete the line to create a new Car object.

var car =  Car();
Multiple choiceeasy

What is the default access level of a class member in C#?

Predict the outputmedium

What does this print?

class Box { public int Size; }
var a = new Box { Size = 1 };
var b = a;
b.Size = 5;
Console.WriteLine(a.Size);
Multiple choicemedium

What does a static field belong to?

Multiple choicehard

Why keep fields private and change them through methods?

C# Object-Oriented Programming Quiz

5 questions to check what you learned.

  1. What is an object?

    • A blueprint
    • A method
    • An instance of a class
    • A namespace
  2. What does this refer to inside a method?

    • The current object
    • The class
    • The previous object
    • The base class
  3. What does == compare for two class instances by default?

    • Their data
    • Their ToString results
    • Their types only
    • Whether they are the same object (reference equality)
  4. What happens when you call a method on a null reference?

    • Nothing
    • A NullReferenceException is thrown
    • It returns null
    • It creates the object
  5. Why override ToString?

    • To compare objects
    • To make it static
    • To make it a string
    • To control how the object appears when printed or shown in the debugger