Skip to content

Intermediate · Classes and objects · topic 15 of 43 · 30 min

Properties: get, set, init and required

Explanation

A property looks like a field from outside but runs code inside: a get accessor to read and a set accessor to write, where you can check values. Auto-properties ({ get; set; }) need no extra code. init lets a value be set only while creating the object, and required forces callers to set it. In C#, public data is normally exposed as properties, not public fields.

Why not public fields?

With a public field, anyone can write account.Balance = -50000; and nothing stops them. A property puts a small gate in front of the data. Callers still write account.Balance, but your code decides what is allowed. You can also change the inside later without breaking the callers.

Think of it like this: A public field is like leaving your shop's cash box open on the counter. A property is like a cashier: customers still pay and get change in the same way, but the cashier checks the notes before accepting them.

Auto-properties

csharp
var c = new Customer { Name = "Asha", Age = 21 };
Console.WriteLine($"{c.Name}, {c.City}, {c.Age}");   // Asha, Unknown, 21

class Customer
{
    public string Name { get; set; } = "";
    public string City { get; set; } = "Unknown";   // default value
    public int Age { get; set; }
}
An auto-property creates a hidden field for you. Properties use PascalCase.

A full property with validation

csharp
var p = new Product();
p.Price = 250m;      // OK
p.Price = -10m;      // throws ArgumentException

class Product
{
    private decimal _price;           // the backing field

    public decimal Price
    {
        get => _price;
        set
        {
            if (value < 0)
                throw new ArgumentException("Price cannot be negative");
            _price = value;
        }
    }
}
Inside set, the keyword value holds the new value being assigned. The private field that stores the data is called the backing field.

C# 14 (with .NET 10) adds the field keyword, which lets a property use its hidden backing field directly so you do not have to declare _price yourself. The classic form above works in every version and is what you will see in most existing code.

Read-only and private set

csharp
class BankAccount
{
    public string AccountNo { get; }                 // read-only: set only in the constructor
    public decimal Balance { get; private set; }     // anyone reads, only this class writes

    public BankAccount(string accountNo) => AccountNo = accountNo;

    public void Deposit(decimal amount)
    {
        if (amount <= 0) throw new ArgumentException("Deposit must be positive");
        Balance += amount;
    }
}
Outside code can read Balance but must go through Deposit to change it.

init: set once, while creating

csharp
var o = new Order { OrderNo = 1001, Customer = "Ravi" };   // OK
// o.OrderNo = 2002;   // error CS8852: init-only property

class Order
{
    public int OrderNo { get; init; }
    public string Customer { get; init; } = "";
}
init (C# 9) allows setting in an object initializer, then the value is fixed. Good for data that should not change after creation.

required: callers must set it

csharp
var ok = new Passenger { Name = "Meena", Age = 34 };
// var bad = new Passenger { Name = "Meena" };   // error: required member 'Age' must be set

class Passenger
{
    public required string Name { get; init; }
    public required int Age { get; init; }
    public string? Berth { get; init; }
}
required (C# 11) makes the compiler check that every required property is set in the object initializer.

Computed properties

csharp
class CartLine
{
    public decimal Price { get; init; }
    public int Qty { get; init; }
    public decimal Total => Price * Qty;      // calculated each time it is read
}
A get-only property with => has no stored data; it calculates its value.
You writeRead outside?Write outside?
{ get; set; }YesYes, any time
{ get; private set; }YesNo (only inside the class)
{ get; }YesNo (only in the constructor)
{ get; init; }YesOnly in the object initializer / constructor
required ... { get; init; }YesMust be set in the object initializer
=> expressionYesNo (computed)

Common mistakes and fixes

  • A property that calls itself: public int Age { get => Age; set => Age = value; } recurses forever and crashes with StackOverflowException. Use a separate backing field or an auto-property.
  • Making every property { get; set; }: think about who should change each value. Prefer private set, init or get-only where you can.
  • Putting slow work (database calls) inside a getter: callers expect properties to be quick. Use a method like LoadOrders() instead.
  • Non-nullable string properties without a value: warning CS8618. Give a default (= ""), set it in the constructor, or mark it required.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Properties: get, set, init and required · 1/7

Properties

In this lesson you will expose data safely with properties.

Prefer a video by a teacher? Find free videos on “C# properties get set init required auto properties tutorial” on YouTube ↗

In real life: Validating a PIN code in a delivery address

Indian PIN codes have exactly 6 digits and do not start with 0. A courier company's address class checks this in the property setter, so a bad PIN can never be stored, no matter which screen or API sets it.

csharp
var addr = new Address { Line1 = "12 MG Road", City = "Indore" };
addr.Pin = "452001";
Console.WriteLine($"{addr.Line1}, {addr.City} - {addr.Pin}");

try { addr.Pin = "0123"; }
catch (ArgumentException ex) { Console.WriteLine(ex.Message); }

class Address
{
    public required string Line1 { get; init; }
    public required string City { get; init; }

    private string _pin = "";
    public string Pin
    {
        get => _pin;
        set
        {
            if (value.Length != 6 || value[0] == '0' || !value.All(char.IsDigit))
                throw new ArgumentException($"Invalid PIN code: {value}");
            _pin = value;
        }
    }
}
Shows 12 MG Road, Indore - 452001, then Invalid PIN code: 0123. try/catch is explained in module 7.

Practice (0/4 done)

Task 1 · Question

Inside a set accessor, what holds the value being assigned?

Task 2 · Question

public decimal Balance { get; private set; } Which code can change Balance?

Task 3 · Question

class T { public int Id { get; init; } } var t = new T { Id = 5 }; t.Id = 6; What happens?

Task 4 · Practice

Write a class MobileRecharge with: required init property Number (string), a Plan property (decimal) whose setter rejects values below 10 or above 5000 with ArgumentException, and a computed property WithGst that returns Plan * 1.18m. Create one object and print WithGst.

Quick quiz

Question 1

1.Why are properties preferred over public fields?

Question 2

2.What does required mean on a property?

Question 3

3.What happens with: public int Age { get => Age; }

Question 4

4.public decimal Total => Price * Qty; What kind of property is this?

Question 5

5.Which accessor allows setting only during object creation?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

Why are properties preferred over public fields?

Answer: They let the class control and validate access, and change internals later

Properties are a controlled gate to the data.

What does required mean on a property?

Answer: The object initializer must set it, or the code does not compile

required is checked by the compiler at the point of creation.

What happens with: public int Age { get => Age; }

Answer: StackOverflowException when read, because the getter calls itself

The getter reads Age, which calls the getter again, forever.

public decimal Total => Price * Qty; What kind of property is this?

Answer: A computed, read-only property

An expression-bodied get-only property calculates its value on every read.

Which accessor allows setting only during object creation?

Answer: init

init (C# 9) is for set-once properties.

What does private set on a property do?Related

Answer: Allows reading from outside but changing only inside the class

The getter stays public; the setter is private.

From Classes and objects

How do you read a @property called total on an object item?Related

Answer: item.total

A property is read like an attribute, without brackets.

From Dataclasses and properties

What makes a class field private?Related

Answer: A leading #

#name fields are truly private; _name is only a naming convention.

From Classes

Which line correctly sets an attribute inside __init__?Related

Answer: self.name = name

Attributes are stored on the object with self.attribute = value.

From Classes and objects

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 2 more topics in “Classes and objects” before its exam: Access modifiers, static members and encapsulation, Structs, value types and reference types.
  2. Module 4 exam: Classes and objects · 13 questions · 20 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, Inheritance, interfaces and records. 43 topics left in the course before the final exam.

← Previous: Classes, objects and constructors