Skip to main content
ANVISoftware Solutions
Lesson 22 of 62Beginner16 min

Classes and Objects

By the end of this lesson

Define a class that keeps related data together, and explain how reference types differ from value types.

Earlier you held employee names in one array and roles in another, and nothing stopped them drifting out of alignment. A class fixes that by keeping related values together in one thing.

A class is a description. An object is one actual item made from that description.

A class, and objects made from it
C#
public class Employee
{
    public string Name { get; set; } = "";
    public string Role { get; set; } = "";
    public decimal AnnualSalary { get; set; }
}

Employee asha = new Employee
{
    Name = "Asha",
    Role = "Engineer",
    AnnualSalary = 1_200_000m
};

Console.WriteLine($"{asha.Name} works as a {asha.Role}.");
  • The class names three pieces of data that belong together. Each is a property.
  • new Employee { ... } creates one object and sets its values.
  • Name and Role cannot now be separated, which removes the whole class of bug the parallel arrays invited.
One collection instead of several
C#
List<Employee> employees = new List<Employee>
{
    new Employee { Name = "Asha",  Role = "Engineer", AnnualSalary = 1_200_000m },
    new Employee { Name = "Ravi",  Role = "Designer", AnnualSalary = 950_000m },
    new Employee { Name = "Meera", Role = "Analyst",  AnnualSalary = 1_050_000m }
};

foreach (Employee employee in employees)
{
    Console.WriteLine($"{employee.Name,-8} {employee.Role,-10} {employee.AnnualSalary:N0}");
}
  • One list, each item carrying all of its own data. Sorting or filtering it cannot desynchronise anything.
  • ,-8 pads to eight characters for aligned output, and :N0 formats the number with thousands separators.

The part that surprises people: reference types

A class is a reference type. A variable of that type does not hold the object itself — it holds a reference to where the object is. Two variables can refer to the same object, and changing it through one is visible through the other.

This catches nearly everyone once, and understanding it now saves a genuinely confusing afternoon later.

Two names, one object
C#
Employee first = new Employee { Name = "Asha", Role = "Engineer" };
Employee second = first;          // not a copy — the same object

second.Role = "Lead Engineer";

Console.WriteLine(first.Role);    // Lead Engineer
Console.WriteLine(second.Role);   // Lead Engineer
  • second = first copies the reference, not the object. Both now point at one Employee.
  • Changing Role through second is therefore visible through first, because there is only one object.

Contrast with a value type, where assignment does copy:

 Value type (int, decimal, bool, struct)Reference type (class, string[], List)
What the variable holdsThe value itselfA reference to the object
Assignment copiesThe valueThe reference
Change via a second variableNot visible in the firstVisible in the first
Can it be null?Only if declared nullableYes
The same pattern with a value type behaves differently
C#
int firstCount = 5;
int secondCount = firstCount;   // a genuine copy

secondCount = 99;

Console.WriteLine(firstCount);    // 5   — unaffected
Console.WriteLine(secondCount);   // 99

Summary

  • A class describes a type; an object is one instance of it
  • Grouping related data in a class removes the risk of parallel collections drifting apart
  • Classes are reference types: assignment copies the reference, not the object
  • Value types copy on assignment; reference types do not
  • To copy an object you must create a new one deliberately

Practice

Attempt each one before opening the solution. Getting it wrong first is how the idea sticks.

Try it yourself

Try it yourself

Create a Product class with a name, a price and a quantity in stock. Build a list of four products and print the total value of the inventory.

Show solution

Each product carries its own three values, so the calculation reads naturally and nothing can drift out of alignment.

C#
public class Product
{
    public string Name { get; set; } = "";
    public decimal Price { get; set; }
    public int QuantityInStock { get; set; }
}

List<Product> products = new List<Product>
{
    new Product { Name = "Notebook",     Price = 120m,  QuantityInStock = 40 },
    new Product { Name = "Pen",          Price = 25m,   QuantityInStock = 200 },
    new Product { Name = "Desk lamp",    Price = 1450m, QuantityInStock = 12 },
    new Product { Name = "Cable tidy",   Price = 300m,  QuantityInStock = 55 }
};

decimal inventoryValue = 0m;

foreach (Product product in products)
{
    inventoryValue += product.Price * product.QuantityInStock;
}

Console.WriteLine($"Inventory value: {inventoryValue:N2}");

Challenge

Challenge

Write a method that takes a Product and applies a 10% discount to its price. Call it, then print the original variable's price.

Explain why the original changed, and then write a version that returns a discounted copy instead of modifying the original.

Show solution

The original changed because the method received a reference to the same object, not a copy. Modifying it inside the method modifies the one object that both the caller and the method can see.

Whether that is desirable depends on intent. A method named ApplyDiscount modifying its argument is arguably reasonable; one named CalculateDiscountedPrice should not. Being deliberate about which you are writing prevents a common category of surprise.

C#
// Modifies the object the caller holds
void ApplyDiscount(Product product)
{
    product.Price *= 0.9m;
}

// Leaves the original untouched and returns a new object
Product WithDiscount(Product product)
{
    return new Product
    {
        Name = product.Name,
        Price = product.Price * 0.9m,
        QuantityInStock = product.QuantityInStock
    };
}

Knowledge check

Nothing is recorded and there is no score. The explanation appears either way.

After `Employee b = a; b.Name = "Changed";` what is a.Name?

Saved in this browser only.