C# (.NET 8 LTS) track

Lesson 06 · Part 2: Object-Oriented Programming

Classes and Objects

Model data and behaviour with classes, constructors and the right access modifiers.

Part 2: Object-Oriented Programming5 min readFree lesson

So far, we have dealt with variables and methods separately, but from now on, we will learn about classes, which deal with data and functions as one. A class is a frame (blueprint) that produces an object, and an instance made from that frame is an object.

Learning Objectives

  • You can define a new type with the class keyword.
  • You can distinguish between fields, methods, and constructors.
  • Know the role of the this keyword
  • Access limiters (public/private/internal/protected) can be used where appropriate.
  • Create objects with new and understand what reference variables actually point to.

Key concepts

1) Classes as blueprints, objects as instances

class Person   //blueprint
{
    public string Name = "";
    public int Age;
}

Person alice = new Person();  //Entity (object) printed as a blueprint
alice.Name = "Alice";
alice.Age = 30;

Person itself has no data in memory. An object is created in memory only when new Person() is called, and the variable alice contains the reference (address) of the object.

2) Fields and methods

  • Field: Data (variable) held by the class
  • method: Action (function) of the class
class Counter
{
    public int Count;                       //field
    public void Increase() => Count++;      //method
}

3) Constructor and this

This is a special method that is automatically called when an object is created. Make it the same as the class name, and do not use the return type.

class Person
{
    public string Name;
    public int Age;

    public Person(string name, int age)
    {
        this.Name = name;   //this.Name is a field, name is a parameter
        this.Age = age;
    }
}

this is a keyword that indicates “the object itself that is currently operating.” It is often used for differentiation when parameter and field names overlap.

4) Access modifiers

Keyword Who can access it?
public Anywhere
private Only within the same class (default)
internal Only within the same assembly (project)
protected Same class + child class

Basics of encapsulation: Expose data as private as much as possible, only operations for external use as public.

5) new keywords 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 also changes

A class is a reference type. Even if you substitute another variable for a variable, the object is not duplicated, and both variables point to the same object.

See as example

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($"Hello, I am {Name}({Age} years old).");
    }
}

Execution result

Hi, I'm Alice (30 years old).
Hi, I'm Bob (25 years old).

Note: We created two independent objects with one Person class.

Example 2 — AccessModifiers: Demonstrating access limiters

// 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 — compilation error
account.LogInternal();           //internal — OK because it’s in the same project
// Account.cs
namespace CodingNow.Lecture.Oop06;

internal class Account
{
    private int balance;             //No direct external changes

    public Account(int initial)
    {
        balance = initial;
    }

    public void Deposit(int amount)  //public action
    {
        if (amount <= 0) return;
        balance += amount;
    }

    public int GetBalance() => balance;

    internal void LogInternal()      //only within the same assembly
    {
        Console.WriteLine($"[Internal Log] Balance={balance}");
    }
}

Execution result

Balance: 1500
[Internal Log] Current balance = 1500

Memo: By blocking balance with private and leaving only Deposit open, you can prevent incorrect changes (negative deposits, etc.).

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) { }            //delegate 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})");
}

Execution result

(0, 0)
(5, 5)
(3, 7)

Memo: : this(...) means "call other constructors of the same class first." Reduces duplicate code.

Example 4 — NewKeyword: Checking the meaning of a reference

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

var a = new Box(10);
var b = a;            //It's not a copy! b also points to the same object.
b.Value = 99;

Console.WriteLine($"a.Value = {a.Value}");
Console.WriteLine($"b.Value = {b.Value}");

var c = new Box(10);  //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;
}

Execution result

a.Value = 99
b.Value = 99
a == b ? True
a == c ? False

Note: b = a is “copy address”. Separate comparison logic (such as Properties/partEquals in the next section) is needed to see if the values ​​themselves are equal.

Common Mistakes

  1. I know that an instance is not created because only new Person is used and () is omitted — the call form must be new Person().
  2. The field and parameter names are the same, but instead of adding this., write a code that substitutes itself (Name = Name;).
  3. If you try to change the private field directly from the outside, you change it to public — broken encapsulation. Solved using the properties in the next section.
  4. Without knowing that the two variables point to the same object, you can rest assured that only one side has been changed.
  5. Name the constructor like Person CreatePerson(), not Person() — it must be exactly the same as the class name.

Cleanup

  • A class is a blueprint, and an object is an entity taken from that blueprint (created with new)
  • Fields are data, methods are actions, and constructors are initialization code when an object is created.
  • this is the current object itself, and private/public controls the external exposure range.
  • Classes are reference types — variables contain the address of an object, not the object itself.

Try it yourself

cd src/PersonBasic
dotnet run

cd ../AccessModifiers
dotnet run

cd ../MultiConstructor
dotnet run

cd ../NewKeyword
dotnet run

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

src/AccessModifiers/AccessModifiers.csprojText
<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.csC#
namespace CodingNow.Lecture.Oop06;

internal class Account
{
    //private: Access only within the same class. Prevents direct changes from outside.
    private int balance;

    public Account(int initial)
    {
        balance = initial;
    }

    //public: action to be used externally
    public void Deposit(int amount)
    {
        if (amount <= 0) return;   //Ignore invalid input
        balance += amount;
    }

    //public: Safe path to check balance
    public int GetBalance() => balance;

    //internal: Visible only within the same assembly (project)
    internal void LogInternal()
    {
        Console.WriteLine($"[Internal Log] Current balance = {balance}");
    }
}
src/AccessModifiers/Program.csC#
using CodingNow.Lecture.Oop06;

var account = new Account(1000);

account.Deposit(500);   //public — can be called from anywhere
Console.WriteLine($"Balance: {account.GetBalance()}");

//account.balance = 9999;   // Because it is a private field, external access is not possible (compilation error when uncommenting)
account.LogInternal();        //internal — OK within the same project
src/MultiConstructor/MultiConstructor.csprojText
<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.csC#
namespace CodingNow.Lecture.Oop06;

internal class Point
{
    public int X;
    public int Y;

    //: this(...) means "call another constructor of the same 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.csC#
using CodingNow.Lecture.Oop06;

//You can select and call constructors with different numbers of arguments in 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.csC#
namespace CodingNow.Lecture.Oop06;

internal class Box
{
    public int Value;

    public Box(int value) => Value = value;
}
src/NewKeyword/NewKeyword.csprojText
<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.csC#
using CodingNow.Lecture.Oop06;

var a = new Box(10);
var b = a;            //Let b also point to the “same object” rather than a copy.
b.Value = 99;

Console.WriteLine($"a.Value = {a.Value}");   //99 (a also changed)
Console.WriteLine($"b.Value = {b.Value}");   // 99

var c = new Box(10);  //A new, completely separate object
Console.WriteLine($"a == b ? {ReferenceEquals(a, b)}");   // True
Console.WriteLine($"a == c ? {ReferenceEquals(a, c)}");   // False
src/PersonBasic/Person.csC#
namespace CodingNow.Lecture.Oop06;

internal class Person
{
    //Field: Data held by an object
    public string Name;
    public int Age;

    //Constructor: Called once when an object is created
    public Person(string name, int age)
    {
        this.Name = name;
        this.Age = age;
    }

    //Method: The behavior of an object
    public void Greet()
    {
        Console.WriteLine($"Hello, I am {Name}({Age} years old).");
    }
}
src/PersonBasic/PersonBasic.csprojText
<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.csC#
using CodingNow.Lecture.Oop06;

//Let's create two objects with the Person class.
var alice = new Person("Alice", 30);
alice.Greet();

var bob = new Person("Bob", 25);
bob.Greet();

Assignment

Problem 1 — Creating the Book class

  • Reference solution: shown below
  • Key concepts: fields, constructors, methods

Requirements

  • Create the Book class. The fields are Title(string), Author(string), and Pages(int).
  • Receives three values from the constructor and initializes them.
  • Describe() method: output in "<Title> - Written by <Author> (<Page>)" format.
  • Create 2~3 books in Program.cs and call Describe().

Expected output

Facts and Misconceptions about Object Orientation - Written by Youngho Cho (240 pages)
Effective C# - by Bill Wagner (320 pages)

Hint

  • Field names should have the first letter capitalized (convention).
  • Assignment like this.Title = title; in the constructor.

Problem 2 — Creating BankAccount

  • Reference solution: shown below
  • Key concepts: private fields, public methods, encapsulation

Requirements

  • Place the private int balance field in the BankAccount class.
  • Creator: Receives the initial balance.
  • Deposit(int amount): Only positive numbers are accepted and added to the balance. Ignoring If-negative/0.
  • Withdraw(int amount): Withdrawal only when it is less than or equal to the balance. If insufficient, output “Insufficient balance”.
  • GetBalance(): Return current balance.
  • Call Program.cs mixed with Deposit/Withdrawal and output the result.

Expected output

Balance: 1500
insufficient balance
Balance: 500

Hint

  • The key is to block balance with private. It must be changed externally only through methods.
  • Invalid values, such as negative deposits, are filtered out within the method.

Check the correct answer

After solving it yourself, compare it with the reference solution below.

Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

Homework01/Book.csC#
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} - Written by {Author} ({Pages} page)");
    }
}
Homework01/Homework01.csprojText
<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>
Homework01/Program.csC#
using CodingNow.Lecture.Oop06;

var b1 = new Book("Facts and misconceptions about object orientation", "Youngho Cho", 240);
var b2 = new Book("Effective C#", "Bill Wagner", 320);

b1.Describe();
b2.Describe();
Homework02/BankAccount.csC#
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 balance");
            return;
        }
        balance -= amount;
    }

    public int GetBalance() => balance;
}
Homework02/Homework02.csprojText
<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>
Homework02/Program.csC#
using CodingNow.Lecture.Oop06;

var acc = new BankAccount(1000);

acc.Deposit(500);
Console.WriteLine($"Balance: {acc.GetBalance()}");

acc.Withdraw(2000);   //Greater than balance (1500) → Output insufficient balance
acc.Withdraw(1000);
Console.WriteLine($"Balance: {acc.GetBalance()}");

Learn this lesson with a mentor

Work through the C# (.NET 8 LTS) track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now