06. Classes and Objects
A class is a user-defined type that bundles data with behavior. Learn fields, methods, constructors, this, and access modifiers; then create objects with new and picture the memory model.
What you'll learn
- 1Define a new type with the `class` keyword
- 2Tell fields, methods, and constructors apart
- 3Know what the `this` keyword does
- 4Use `public`/`private`/`internal`/`protected` access modifiers correctly
- 5Create objects with `new` and understand what a reference variable actually points to
Overview
Up to now we used variables and methods separately β from here on we bundle data and behavior together in a **class**. A class is a **blueprint** that stamps out objects; an instance made from that blueprint is an **object**.
Core Concepts
1) Class = blueprint, object = instance
class Person // blueprint
{
public string Name = "";
public int Age;
}
Person alice = new Person(); // an actual instance stamped from the blueprint
alice.Name = "Alice";
alice.Age = 30;`Person` by itself holds no data in memory. Only when you call `new Person()` does an object get created in memory, and the variable `alice` holds the **reference (address)** to that object.
2) Fields and methods
- **Field**: data (a variable) that the class owns
- **Method**: behavior (a function) that the class owns
class Counter
{
public int Count; // field
public void Increase() => Count++; // method
}3) Constructor and `this`
A special method that runs automatically when an object is created. It has **the same name as the class** and no return type.
class Person
{
public string Name;
public int Age;
public Person(string name, int age)
{
this.Name = name; // this.Name is the field, name is the parameter
this.Age = age;
}
}`this` refers to "the current object running this code." It's commonly used to disambiguate parameters and fields that share a name.
4) Access modifiers
| Keyword | Who can access? |
|---|---|
| `public` | anywhere |
| `private` | only inside the same class (default) |
| `internal` | only inside the same assembly (project) |
| `protected` | the class itself + subclasses |
Encapsulation basics: **keep data as `private` as possible**, **expose only the operations you want callable as `public`**.
5) The `new` keyword and references
Person a = new Person("Alice", 30);
Person b = a; // b points to the same object as a (not a copy!)
b.Age = 99;
Console.WriteLine(a.Age); // 99 β a changed tooA class is a **reference type**. Assigning one variable to another does not clone the object β both variables point to the same object.
Examples
Example 1 β `PersonBasic`: the simplest class
// Program.cs
using CodingNow.Lecture.Oop06;
var alice = new Person("Alice", 30);
alice.Greet();
var bob = new Person("Bob", 25);
bob.Greet();// Person.cs
namespace CodingNow.Lecture.Oop06;
internal class Person
{
public string Name;
public int Age;
public Person(string name, int age)
{
this.Name = name;
this.Age = age;
}
public void Greet()
{
Console.WriteLine($"Hi, I'm {Name} ({Age}).");
}
}**Output**
Hi, I'm Alice (30).
Hi, I'm Bob (25).**Note:** With a single `Person` class we stamped out two independent objects.
Example 2 β `AccessModifiers`: access modifier demo
// Program.cs
using CodingNow.Lecture.Oop06;
var account = new Account(1000);
account.Deposit(500); // public β OK
Console.WriteLine($"Balance: {account.GetBalance()}");
// account.balance = 0; // private β compile error
account.LogInternal(); // internal β OK within the same project// Account.cs
namespace CodingNow.Lecture.Oop06;
internal class Account
{
private int balance; // cannot be changed directly from outside
public Account(int initial)
{
balance = initial;
}
public void Deposit(int amount) // exposed operation
{
if (amount <= 0) return;
balance += amount;
}
public int GetBalance() => balance;
internal void LogInternal() // only within the same assembly
{
Console.WriteLine($"[internal log] balance={balance}");
}
}**Output**
Balance: 1500
[internal log] current balance = 1500**Note:** Locking `balance` behind `private` and exposing only `Deposit` prevents invalid mutations (e.g. negative deposits).
Example 3 β `MultiConstructor`: constructor overloading + `this(...)`
// Program.cs
using CodingNow.Lecture.Oop06;
var p1 = new Point(); // (0, 0)
var p2 = new Point(5); // (5, 5)
var p3 = new Point(3, 7); // (3, 7)
p1.Print();
p2.Print();
p3.Print();// Point.cs
namespace CodingNow.Lecture.Oop06;
internal class Point
{
public int X;
public int Y;
public Point() : this(0, 0) { } // delegates to another constructor
public Point(int v) : this(v, v) { } // one value for both X and Y
public Point(int x, int y)
{
X = x;
Y = y;
}
public void Print() => Console.WriteLine($"({X}, {Y})");
}**Output**
(0, 0)
(5, 5)
(3, 7)**Note:** `: this(...)` means "first call another constructor of the same class." Helps you remove duplicate code.
Example 4 β `NewKeyword`: confirm reference semantics
// Program.cs
using CodingNow.Lecture.Oop06;
var a = new Box(10);
var b = a; // not a copy! b points to the same object as a.
b.Value = 99;
Console.WriteLine($"a.Value = {a.Value}");
Console.WriteLine($"b.Value = {b.Value}");
var c = new Box(10); // a completely different object
Console.WriteLine($"a == b ? {ReferenceEquals(a, b)}");
Console.WriteLine($"a == c ? {ReferenceEquals(a, c)}");// Box.cs
namespace CodingNow.Lecture.Oop06;
internal class Box
{
public int Value;
public Box(int value) => Value = value;
}**Output**
a.Value = 99
b.Value = 99
a == b ? True
a == c ? False**Note:** `b = a` "copies the address." To compare whether values are equal you need separate logic (properties / `Equals`, covered in the next lecture).
Full example code (src/)
src/AccessModifiers/AccessModifiers.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.Oop06</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
src/AccessModifiers/Account.cs
namespace CodingNow.Lecture.Oop06;
internal class Account
{
// private: only accessible inside this class. Prevents external mutation.
private int balance;
public Account(int initial)
{
balance = initial;
}
// public: operation callable from outside
public void Deposit(int amount)
{
if (amount <= 0) return; // ignore invalid input
balance += amount;
}
// public: a safe way to read the balance
public int GetBalance() => balance;
// internal: only visible inside the same assembly (project)
internal void LogInternal()
{
Console.WriteLine($"[internal log] current balance = {balance}");
}
}
src/AccessModifiers/Program.cs
using CodingNow.Lecture.Oop06;
var account = new Account(1000);
account.Deposit(500); // public β callable anywhere
Console.WriteLine($"Balance: {account.GetBalance()}");
// account.balance = 9999; // private field β external access blocked (uncommenting -> compile error)
account.LogInternal(); // internal β OK within the same project
src/MultiConstructor/MultiConstructor.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.Oop06</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
src/MultiConstructor/Point.cs
namespace CodingNow.Lecture.Oop06;
internal class Point
{
public int X;
public int Y;
// ': this(...)' means "call another constructor of this class first."
public Point() : this(0, 0) { }
public Point(int v) : this(v, v) { }
public Point(int x, int y)
{
X = x;
Y = y;
}
public void Print() => Console.WriteLine($"({X}, {Y})");
}
src/MultiConstructor/Program.cs
using CodingNow.Lecture.Oop06;
// You can pick a constructor by argument count within the same class.
var p1 = new Point(); // (0, 0)
var p2 = new Point(5); // (5, 5)
var p3 = new Point(3, 7); // (3, 7)
p1.Print();
p2.Print();
p3.Print();
src/NewKeyword/Box.cs
namespace CodingNow.Lecture.Oop06;
internal class Box
{
public int Value;
public Box(int value) => Value = value;
}
src/NewKeyword/NewKeyword.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.Oop06</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
src/NewKeyword/Program.cs
using CodingNow.Lecture.Oop06;
var a = new Box(10);
var b = a; // not a copy β b is pointed at the "same object."
b.Value = 99;
Console.WriteLine($"a.Value = {a.Value}"); // 99 (a changed too)
Console.WriteLine($"b.Value = {b.Value}"); // 99
var c = new Box(10); // a completely separate new object
Console.WriteLine($"a == b ? {ReferenceEquals(a, b)}"); // True
Console.WriteLine($"a == c ? {ReferenceEquals(a, c)}"); // False
src/PersonBasic/Person.cs
namespace CodingNow.Lecture.Oop06;
internal class Person
{
// fields: data the object holds
public string Name;
public int Age;
// constructor: runs once when the object is created
public Person(string name, int age)
{
this.Name = name;
this.Age = age;
}
// method: behavior the object provides
public void Greet()
{
Console.WriteLine($"Hi, I'm {Name} ({Age}).");
}
}
src/PersonBasic/PersonBasic.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.Oop06</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
src/PersonBasic/Program.cs
using CodingNow.Lecture.Oop06;
// Make two objects from the Person class.
var alice = new Person("Alice", 30);
alice.Greet();
var bob = new Person("Bob", 25);
bob.Greet();
Common Mistakes
- Writing just `new Person` (no `()`) and assuming no instance is created β it must be `new Person()`.
- Forgetting `this.` when field and parameter share a name, so you assign a parameter to itself (`Name = Name;`).
- Making a `private` field `public` just to mutate it from outside β breaks encapsulation. Use properties (next lecture).
- Assuming two variables point to different objects when they don't.
- Naming a constructor `Person CreatePerson()` instead of `Person()` β it must exactly match the class name.
Summary
- Class is the blueprint, object is an instance built from it (created with `new`)
- Fields are data, methods are behavior, constructors initialize the object
- `this` is the current object; `private`/`public` controls outside visibility
- Classes are reference types β the variable holds the address, not the object itself
Practice
**Practice - 06. Classes and Objects**
Problem 1 β Build a `Book` class
- Project folder: `Homework01/`
- Key concepts: fields, constructor, method
Requirements
- Build a `Book` class with fields `Title`(string), `Author`(string), `Pages`(int).
- The constructor accepts the three values and initializes the fields.
- `Describe()` method: print as `"<Title> by <Author> (<Pages>p)"`.
- In `Program.cs` create 2-3 books and call `Describe()`.
Expected output
Object-Oriented Reality by Cho Younghoh (240p)
Effective C# by Bill Wagner (320p)Hints
- Capitalize field names (convention).
- Use `this.Title = title;` inside the constructor.
Problem 2 β Build a `BankAccount`
- Project folder: `Homework02/`
- Key concepts: `private` field, `public` method, encapsulation
Requirements
- `BankAccount` class has a `private int balance` field.
- Constructor takes the initial balance.
- `Deposit(int amount)`: only adds positive amounts. Ignores 0 / negatives.
- `Withdraw(int amount)`: withdraws only when the amount β€ balance. If insufficient, print `"Insufficient funds"`.
- `GetBalance()`: returns the current balance.
- In `Program.cs` mix deposits/withdrawals and print the result.
Expected output
Balance: 1500
Insufficient funds
Balance: 500Hints
- The whole point is that `balance` is `private`. Outside callers can only change it via methods.
- Filter bad input (negative deposits) inside the method.
Check your answer
Try it yourself, then compare against the [`answer/`](./answer/) folder.
Answer (answer/)
homework/answer/Homework01/Book.cs
namespace CodingNow.Lecture.Oop06;
internal class Book
{
public string Title;
public string Author;
public int Pages;
public Book(string title, string author, int pages)
{
this.Title = title;
this.Author = author;
this.Pages = pages;
}
public void Describe()
{
Console.WriteLine($"{Title} by {Author} ({Pages}p)");
}
}
homework/answer/Homework01/Homework01.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.Oop06</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
homework/answer/Homework01/Program.cs
using CodingNow.Lecture.Oop06;
var b1 = new Book("Object-Oriented Reality", "Cho Younghoh", 240);
var b2 = new Book("Effective C#", "Bill Wagner", 320);
b1.Describe();
b2.Describe();
homework/answer/Homework02/BankAccount.cs
namespace CodingNow.Lecture.Oop06;
internal class BankAccount
{
private int balance;
public BankAccount(int initial)
{
balance = initial;
}
public void Deposit(int amount)
{
if (amount <= 0) return;
balance += amount;
}
public void Withdraw(int amount)
{
if (amount > balance)
{
Console.WriteLine("Insufficient funds");
return;
}
balance -= amount;
}
public int GetBalance() => balance;
}
homework/answer/Homework02/Homework02.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.Oop06</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
homework/answer/Homework02/Program.cs
using CodingNow.Lecture.Oop06;
var acc = new BankAccount(1000);
acc.Deposit(500);
Console.WriteLine($"Balance: {acc.GetBalance()}");
acc.Withdraw(2000); // exceeds balance(1500) β prints Insufficient funds
acc.Withdraw(1000);
Console.WriteLine($"Balance: {acc.GetBalance()}");
Try It Yourself
cd src/PersonBasic
dotnet run
cd ../AccessModifiers
dotnet run
cd ../MultiConstructor
dotnet run
cd ../NewKeyword
dotnet runNext Lecture
[07_Properties_and_Encapsulation](../07_%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0%EC%99%80_%EC%BA%A1%EC%8A%90%ED%99%94/) β Use properties instead of `public` fields for safer data access.
All lecture materials and example code are openly available on GitHub.
View on GitHub β