Back to Blog
C#

C# Auto-Implemented Property: Syntax and Usage

c# auto implemented property: Learn how C# auto-implemented properties work, how to initialize them, when to use them, and their tradeoffs against manual property decl...

C#PropertiesAuto-Implemented PropertiesC# SyntaxImmutable Objects
Diagram illustrating C# auto-implemented property with compiler-generated backing field and get/set accessors.

How Auto-Implemented Properties Work

When you declare a property like this:

public int Age { get; set; }

the C# compiler generates a private anonymous backing field and implements the getter and setter to read and write that field. You never see the field, but it exists in the compiled assembly. This is the core of the c# auto implemented property feature: it removes the boilerplate of declaring a field and wiring up accessors manually.

The generated backing field is not accessible from your code, which prevents accidental direct access. The property becomes the only way to read or modify the stored value, unless you use reflection.

Declaring Auto-Implemented Properties

The basic syntax is straightforward:

public string Name { get; set; } public int Id { get; set; } public bool IsActive { get; set; }

You can also declare a property with only a getter:

public DateTime CreatedAt { get; }

This creates a read-only property. The backing field is still generated, but it can only be assigned from within the constructor or via a property initializer.

C# 9 introduced the init accessor, which allows assignment only during object initialization:

public string Title { get; init; }

This is useful for immutable objects where you want to set values once at construction time and then prevent modification.

Initializing Auto-Implemented Properties

You can assign a default value directly at the declaration:

public int Count { get; set; } = 10; public string Label { get; set; } = "Default";

This initializer runs before the constructor body. For read-only properties, you can use an initializer or assign in the constructor:

public class Report { public DateTime GeneratedAt { get; } = DateTime.UtcNow; public string Author { get; } public Report(string author) { Author = author; } }

For init properties, you can set them in the constructor or via an object initializer:

var item = new Product { Name = "Laptop", Price = 1200.00m };

Read-Only and Immutable Properties

Read-only auto-implemented properties (get; only) are a common way to expose data that should not change after construction. Combined with an initializer or constructor assignment, they help create immutable types.

The init accessor extends this pattern by allowing assignment during object initialization, including in collection initializers and with named arguments. This is particularly useful for record types and DTOs where you want a concise, immutable definition.

When to Use Auto-Implemented Properties vs. Manual Properties

Auto-implemented properties are ideal when you need a simple data holder with no additional logic. They reduce noise and make the code easier to read. However, if you need to validate values, compute a value on the fly, or implement lazy loading, you should use a manual property with an explicit backing field.

For example:

private int _age; public int Age { get => _age; set { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value)); _age = value; } }

This cannot be done with an auto-implemented property without adding a separate validation method or using a field-backed property.

Common Pitfalls and Limitations

Auto-implemented properties have a few limitations:

  • You cannot add logic to the getter or setter without converting to a manual property.
  • The backing field is not accessible, so you cannot use ref or out on the property.
  • The default equality behavior for structs may be affected if you rely on property values, but that is not specific to auto-implemented properties.
  • Serialization frameworks may treat auto-implemented properties differently depending on whether they have a setter or only a getter.

If you need to control the backing field type or add attributes to it, you must use a manual property.

Performance and Runtime Behavior

Auto-implemented properties do not introduce any measurable runtime overhead compared to manually implemented properties. The compiler generates the same IL for the getter and setter as you would write manually. The only difference is that the backing field is anonymous and inaccessible.

One subtle point: the generated backing field is a real field, so it participates in memory layout and lifetime just like any other field. For value types, the property accessor simply reads or writes the field; for reference types, it reads or writes the reference. There is no boxing or extra indirection.

Maintainability and Code Style

Using auto-implemented properties consistently can improve maintainability by reducing boilerplate. When a property later needs validation or computed behavior, you can convert it to a manual property without changing the public API. This refactoring is straightforward and does not affect callers.

However, if you know from the start that a property will require logic, it is often clearer to write the manual property immediately. The decision should be based on the current and likely future requirements, not on a rule that auto-implemented properties are always better.

c# auto implemented property: Practical Usage and Code Examp | RYUSLOG DEV