- Explain classes and objects and create objects with
new - Protect data with properties,
private set,initandrequired - Write ordinary and primary constructors
- Use
recordtypes and thewithexpression
You are writing a banking app: every account has an owner and a balance, you can deposit and withdraw money, and the balance must never go negative. For thousands of accounts, separate variables and methods would quickly become a mess. C# is an object-oriented language: data and the methods that work with it are bundled in a class, and then you create as many objects from that class as you need.
Class and object
A class is a blueprint for objects: it describes what data each object will have and what it can do. An object is a concrete instance built from that blueprint with the new keyword. In a file that uses top-level statements, classes are written after the statements, at the end of the file.
var account = new BankAccount("Leyla");
account.Deposit(100);
account.Withdraw(30);
account.Withdraw(500);
Console.WriteLine($"{account.Owner}: {account.Balance} AZN");
var other = new BankAccount("Elvin");
Console.WriteLine($"{other.Owner}: {other.Balance} AZN");
class BankAccount
{
public string Owner { get; }
public decimal Balance { get; private set; }
public BankAccount(string owner)
{
Owner = owner;
}
public void Deposit(decimal amount) => Balance += amount;
public void Withdraw(decimal amount)
{
if (amount <= Balance) Balance -= amount;
else Console.WriteLine("Not enough money!");
}
}Not enough money! Leyla: 70 AZN Elvin: 0 AZN
| Term | Meaning | In the example |
|---|---|---|
| Class | the blueprint for objects | BankAccount |
| Object | an instance made from a class | account, other |
| Property | a piece of the object's data with controlled reading and writing | Balance |
| Method | something the object can do | Deposit() |
| Constructor | a special method that runs when an object is created | BankAccount(string owner) |
A constructor has the same name as its class and no return type. When you write new BankAccount("Leyla"), the object is created and the constructor is called immediately. account and other come from the same class, but each has its own balance — objects are independent of each other.
Properties and encapsulation
A public member can be seen from anywhere, while a private member is only available inside the class. The property Balance { get; private set; } can be read from outside, but only the class's own methods can change it. If you write account.Balance = 1000000;, the compiler reports error CS0272. Protecting data like this is called encapsulation: the balance changes only through the rules in Deposit and Withdraw.
You can also create an object with an object initializer: properties get their values inside curly braces. init allows a property to be set only while the object is being created, and required demands that it must be filled in. A property whose body is written with => is a computed property: its value is recalculated every time.
var phone = new Product { Name = "Phone", Price = 450m, Stock = 3 };
var cable = new Product { Name = "Cable", Price = 12m };
Console.WriteLine($"{phone.Name}: {phone.TotalValue} AZN, in stock: {phone.InStock}");
Console.WriteLine($"{cable.Name}: {cable.TotalValue} AZN, in stock: {cable.InStock}");
phone.Stock--;
Console.WriteLine($"{phone.Name} left: {phone.Stock}");
class Product
{
public required string Name { get; init; }
public decimal Price { get; init; }
public int Stock { get; set; }
public decimal TotalValue => Price * Stock;
public bool InStock => Stock > 0;
}Phone: 1350 AZN, in stock: True Cable: 0 AZN, in stock: False Phone left: 2
new Product { Price = 5m } without Name gives error CS9035.Primary constructors
Since C# 12 you can write the constructor's parameters right after the class name — this is a primary constructor. The parameters are visible in all members of the class, so there is no need for a separate constructor. In the example below, the ToString method defines how the object looks as text: Console.WriteLine(hero) calls exactly that. You will learn about the word override in the next lesson.
var hero = new Player("Aysel");
hero.GainXp(250);
Console.WriteLine(hero);
hero.GainXp(80);
Console.WriteLine(hero);
class Player(string name)
{
public int Level { get; private set; } = 1;
public int Xp { get; private set; }
public void GainXp(int amount)
{
Xp += amount;
Level = 1 + Xp / 100;
}
public override string ToString() => $"{name}: level {Level}, {Xp} XP";
}Aysel: level 3, 250 XP Aysel: level 4, 330 XP
Records: types for data
Sometimes an object only carries data: the coordinates of a point, the date and total of an order. For such cases C# has records. record Point(int X, int Y); creates, in one line, the properties, a constructor, a readable ToString and comparison by value. A record's properties cannot be changed, but the with expression makes a new copy with some properties different.
var p1 = new Point(3, 4);
var p2 = new Point(3, 4);
var p3 = p1 with { Y = 10 };
Console.WriteLine(p1);
Console.WriteLine(p3);
Console.WriteLine(p1 == p2);
var c1 = new PointClass(3, 4);
var c2 = new PointClass(3, 4);
Console.WriteLine(c1 == c2);
record Point(int X, int Y);
class PointClass(int x, int y)
{
public int X => x;
public int Y => y;
}Point { X = 3, Y = 4 }
Point { X = 3, Y = 10 }
True
FalseKey points
- A class is a blueprint; an object is a concrete instance created with
new. - A constructor runs when an object is created; a primary constructor's parameters follow the class name.
private set,initandrequiredprotect data — this is encapsulation.- Classes are reference types: two variables can point to the same object.
- A
recordis for immutable data: comparison by value, a readyToStringandwith.
Check yourself
10 questions. Every correct answer earns XP.
account.Withdraw(500) not change the balance?