BuildQuill
C# learning path
15 of 25
Lesson 15 of 25
Lesson 15Beginner20 min

C# Constructors, Properties, and Encapsulation

Create objects in valid states, control how state changes, and design a small public surface that protects domain rules.

Before this lesson

  • Classes and objects
  • Methods and conditions
  • Nullable values

Require valid starting data in a constructor

Use properties and access modifiers intentionally

Protect invariants through behavior methods

The short answer

A constructor establishes an object's starting state. Properties expose controlled access to values. Encapsulation keeps state changes behind operations that enforce the object's rules instead of letting any caller assign anything.

01

An object should not begin half-valid

The first Product example created an empty object and assigned fields afterward. Between those steps the object had a null name and zero price, whether those values made sense or not. A constructor receives the information required to establish a valid starting state before the reference is returned to the caller.

A constructor has the same name as the class and no return type. new BankAccount("Amina", 100m) allocates the object and calls the matching constructor. If validation fails, object creation fails instead of releasing an invalid account into the program.

02

Properties are an access contract

A property looks like field access to a caller but is implemented through get and set accessors. An auto-property such as public string Owner { get; private set; } lets any caller read Owner while limiting assignment to the BankAccount class.

Properties can compute a value, validate assignment, or restrict setters, but avoid hiding surprising expensive or state-changing work in a getter. A property should feel like reading or writing a characteristic; an action belongs in a method.

MemberUse forControl
FieldPrivate implementation state or simple data structureDirect storage access
PropertyPublic characteristic or controlled valueget/set accessors
MethodAction, decision, or potentially substantial workParameters and return value
03

Encapsulation protects invariants

An invariant is a rule that should remain true whenever an object is available for use. For the account, Balance must not be negative. A public setter would allow account.Balance = -1000m from anywhere, bypassing the withdrawal rule. A private setter and a Withdraw method keep every valid change on a controlled path.

Encapsulation is not making every member private without thought. It means the type exposes what callers need while retaining enough control to keep its promises. Start with the narrowest useful access and widen it when a real collaborator requires more.

C#
public void Deposit(decimal amount)
{
  if (amount <= 0m)
      throw new ArgumentOutOfRangeException("amount");

  Balance += amount;
}
04

Decide how invalid operations are reported

The constructor throws for invalid starting arguments because a valid account cannot be created without them. Withdraw returns false for an ordinary rejected request such as insufficient funds. These are design choices: exceptions suit contract violations or failures a local caller cannot reasonably ignore, while return values suit expected alternative outcomes.

Do not mix policies randomly across similar operations. Consistent behavior helps callers use a type safely. When failure needs more information than true or false, return a focused result type or provide a Try-style method with clear output.

Good habits

  • Require essential values in the constructor.
  • Avoid public setters for state whose changes have rules or side effects.
  • Keep validation close to the operation that owns the rule.
  • Expose the smallest public API that supports real callers.

Quick knowledge check

Answer before you reveal.

01Why is public decimal Balance { get; private set; } safer than a public setter?

External callers can read the balance but must use the class's operations to change it, so its rules cannot be bypassed directly.

02Should every rejected user action throw an exception?

No. Expected outcomes such as an already-borrowed book can be represented by a Boolean or result value; exceptions are not ordinary branching tools.

Practice challenge

Now build it without copying.

Turn the Book class into a valid model. Require title and author in a constructor, expose IsBorrowed with a private setter, and make Borrow return false when already borrowed.

You are done when

  • A Book cannot be created without required text
  • Callers cannot assign IsBorrowed directly
  • Borrow and Return preserve a consistent state

Stretch: Add a BorrowerName property that is null when returned and required while borrowed. Keep the two properties consistent through methods.

Open challenge in playground

Lesson checkpoint

One small step locks it in

Mark this lesson complete, then keep the momentum going.

Complete and continue

Clear up the details

Frequently asked questions

Can a class have more than one constructor?

Yes, when parameter lists differ. Keep each overload's meaning clear and route shared initialization through one consistent path.

What is init?

In modern C#, an init accessor allows assignment during object initialization but not ordinary later mutation. It is useful for immutable data models and records.

Are public fields always wrong?

No. They can be appropriate in small data-oriented or interop types, but properties give public object APIs more room to evolve and control access.