Back to Blog
C#

C# Private Set: Syntax and Use Cases

c# private set: Understand the private set accessor in C# properties: syntax, constructor usage, and when to prefer init or readonly.

C#PropertiesEncapsulationAccess ModifiersObject-Oriented Design
Diagram showing a C# property with a public getter and private setter, illustrating controlled assignment.

c# private set requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

The private set accessor in C# lets a property expose a public getter while restricting assignment to code inside the class. It is a common way to implement read-only properties that still need to be modified internally. This article covers the syntax, how it works with constructors, and when to prefer init or readonly fields instead.

What private set Actually Does

A property with private set has a public getter and a private setter. The setter can only be invoked from within the containing class, not from external code. This means external consumers can read the property value, but only the class itself can change it. The access modifier on the setter applies to the setter method, not to the property as a whole.

This pattern is useful when you want to expose a value that the class must be able to update internally, but that should remain immutable from the outside. For example, an Order class might expose a Total amount that can be discounted internally but not set arbitrarily by callers.

Basic Syntax and Examples

The syntax is straightforward: place private before the set keyword in the property declaration.

public class Order { public string OrderId { get; private set; } public decimal Total { get; private set; } public Order(string orderId, decimal total) { OrderId = orderId; Total = total; } public void ApplyDiscount(decimal discount) { Total -= discount; } }

In this example, OrderId and Total can be read from anywhere, but only the Order class can assign them. The constructor assigns initial values, and the ApplyDiscount method modifies Total. External code cannot do order.Total = 100 because the setter is private.

Assigning Values Inside the Class

The private setter is accessible from any method, constructor, or property within the same class. This includes instance methods, static methods, and constructors. It does not allow assignment from derived classes unless those derived classes are within the same class body, which they are not. If you need derived classes to set the value, consider protected set instead.

A common use case is a class that maintains its own state and exposes a read-only view to the outside. For example, a BankAccount class might expose a Balance property that only the class can change through deposit and withdrawal methods.

public class BankAccount { public decimal Balance { get; private set; } public void Deposit(decimal amount) => Balance += amount; public void Withdraw(decimal amount) => Balance -= amount; }

Here, the balance is only modified through the class's own methods, preventing invalid states like negative balances from being set directly.

When to Use private set Instead of init

C# 9 introduced init accessors, which allow a property to be set only during object initialization. The key difference is that init restricts assignment to the constructor or object initializer, while private set allows assignment at any time inside the class. If you need to modify the property after construction, private set is the right choice. If the value should be fixed once the object is created, init is more restrictive and communicates that intent more clearly.

For example, an immutable Person class with a Name property that never changes after construction is better modeled with init:

public class Person { public string Name { get; init; } }

This prevents even the class itself from changing Name after initialization. If you need internal mutation, use private set.

When to Use a readonly Field Instead

For a field that is assigned only in the constructor and never changed afterward, a readonly field is more restrictive than a property with private set. A readonly field cannot be assigned outside the constructor, not even by methods within the class. If you need a property that is read-only externally but mutable internally, private set is appropriate. If you need a value that is truly immutable after construction, a readonly field combined with a read-only property is a common pattern.

public class Configuration { private readonly int _maxRetries; public int MaxRetries => _maxRetries; public Configuration(int maxRetries) { _maxRetries = maxRetries; } }

This gives you a property with no setter at all, which is even more restrictive than private set. The choice depends on whether the class itself needs to modify the value later.

Constructor and Object Initializer Behavior

A property with private set can be assigned in the constructor of the same class. It cannot be assigned via an object initializer from outside the class, because object initializers require a public or accessible setter. This is an important distinction: init allows object initializer assignment, while private set does not.

// This works with init, but not with private set var person = new Person { Name = "Alice" };

If you need to support object initializer syntax, use init. If you need internal mutability, use private set and rely on constructors or methods to set initial values.

Common Mistakes and Edge Cases

One common mistake is forgetting to assign a private set property in the constructor, leaving it with the default value. This is especially problematic for value types like int or decimal, which default to zero. Always initialize the property in the constructor or via a method that is guaranteed to run before the property is read.

Another edge case is using private set on a property that is part of an interface. Interface properties must have public accessors, so you cannot implement an interface property with a private setter. The interface defines the contract, and the implementation must match the accessor accessibility.

Reflection can bypass access modifiers, so private set is not a security boundary. It is a design tool that communicates intent and prevents accidental modification, not a defense against malicious code.

Maintainability and Design Considerations

The private set accessor supports encapsulation by keeping the write path inside the class. This makes it easier to enforce invariants, because all modifications go through the class's own logic. For example, you can validate a new value before assigning it, or you can ensure that related properties are updated together.

Using private set also reduces the surface area of the public API. External code cannot accidentally set a property to an invalid value, which simplifies the contract and reduces the need for defensive checks in every caller. When a property needs to be read-only but mutable internally, private set is often the clearest way to express that.

However, it adds a small amount of boilerplate compared to a public setter, and it can make the class harder to test if you need to change the value from a test. In those cases, you might expose a test-only method or use an internal setter with InternalsVisibleTo. Weigh these tradeoffs against the encapsulation benefits when designing your classes.

c# private set: Practical Usage and Code Examples | RYUSLOG DEV