Auto-Implemented Properties in C# Explained

Auto-implemented properties in C# provide a concise syntax for declaring class properties without needing to explicitly define a private backing field. This article explains how auto-implemented properties work, their syntax, how to initialize them, control their access levels, and when to choose them over traditional properties.

The Purpose of Auto-Implemented Properties

In object-oriented programming, encapsulation is typically achieved using getters and setters to control access to class fields. In older versions of C#, developers had to manually declare a private backing field alongside public getter and setter methods or full property syntax.

Auto-implemented properties streamline this by allowing you to declare a property in a single line. When the C# compiler encounters an auto-implemented property, it automatically creates a hidden, private backing field that can only be accessed through the property's accessors.

Basic Syntax Comparison

Traditional Property with Backing Field:

public class User
{
    private string _name;

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

Auto-Implemented Property:

public class User
{
    public string Name { get; set; }
}

Both examples produce functionally identical code after compilation, but the auto-implemented syntax eliminates boilerplate code.

Property Initializers

You can initialize auto-implemented properties with default values directly in the declaration:

public class Product
{
    public string Name { get; set; } = "Unnamed Product";
    public decimal Price { get; set; } = 0.0m;
}

These default values are assigned when an instance of the class is constructed.

Read-Only and Init-Only Properties

Auto-implemented properties support immutability using read-only accessors or the init accessor.

  • Read-only properties: By omitting the set accessor, the property can only be set via the declaration initializer or within the class constructor.
public class Order
{
    public Guid Id { get; } = Guid.NewGuid();

    public Order(Guid customId)
    {
        Id = customId; // Allowed in constructor
    }
}
  • Init-only properties (C# 9.0+): Using init allows the property to be assigned only during object initialization:
public class Customer
{
    public int CustomerId { get; init; }
}

// Usage
var customer = new Customer { CustomerId = 101 };
// customer.CustomerId = 102; // Compilation error

Access Modifiers on Accessors

You can restrict the visibility of the get or set accessor independently by adding access modifiers:

public class BankAccount
{
    public decimal Balance { get; private set; }

    public void Deposit(decimal amount)
    {
        if (amount > 0)
        {
            Balance += amount;
        }
    }
}

In this case, any external code can read Balance, but only code inside the BankAccount class can modify it.

When to Use Auto-Implemented Properties

Auto-implemented properties are best used when:

  • No custom logic or validation is needed in the get or set accessors.
  • You are building Data Transfer Objects (DTOs), models, or entity types.
  • You want clean, readable code with minimal boilerplate.

If you need to validate input (such as throwing an ArgumentOutOfRangeException), trigger change notification events (like INotifyPropertyChanged), or transform values before storage, use a full property with an explicit backing field instead.