← Back to C# series
🧩
OOP
OOP Β· Prerequisite: methods

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.

C#.NET 8OOPclass
Duration
⏱ ~1-1.5 hours
Level
πŸ“Š Intermediate
Prerequisite
🎯 Methods completed
OUTCOME
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

csharp
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
csharp
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.

csharp
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

KeywordWho 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

csharp
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 too

A 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

csharp
// Program.cs
using CodingNow.Lecture.Oop06;

var alice = new Person("Alice", 30);
alice.Greet();

var bob = new Person("Bob", 25);
bob.Greet();
csharp
// 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**

text
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

csharp
// 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
csharp
// 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**

text
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(...)`

csharp
// 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();
csharp
// 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**

text
(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

csharp
// 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)}");
csharp
// Box.cs
namespace CodingNow.Lecture.Oop06;

internal class Box
{
    public int Value;
    public Box(int value) => Value = value;
}

**Output**

text
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

xml
<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

csharp
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

csharp
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

xml
<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

csharp
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

csharp
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

csharp
namespace CodingNow.Lecture.Oop06;

internal class Box
{
    public int Value;

    public Box(int value) => Value = value;
}

src/NewKeyword/NewKeyword.csproj

xml
<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

csharp
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

csharp
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

xml
<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

csharp
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

  1. Writing just `new Person` (no `()`) and assuming no instance is created β€” it must be `new Person()`.
  2. Forgetting `this.` when field and parameter share a name, so you assign a parameter to itself (`Name = Name;`).
  3. Making a `private` field `public` just to mutate it from outside β€” breaks encapsulation. Use properties (next lecture).
  4. Assuming two variables point to different objects when they don't.
  5. 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

text
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

text
Balance: 1500
Insufficient funds
Balance: 500

Hints

  • 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

csharp
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

xml
<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

csharp
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

csharp
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

xml
<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

csharp
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

bash
cd src/PersonBasic
dotnet run

cd ../AccessModifiers
dotnet run

cd ../MultiConstructor
dotnet run

cd ../NewKeyword
dotnet run

Next 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.

Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub β†—