C# Field vs Property: When to Use Each
c# field vs property: Understand the difference between fields and properties in C#, including syntax, encapsulation, validation, and performance tradeoffs.
c# field vs property requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
Consider a simple class that stores a temperature value. You could expose it as a public field or as a property. The choice looks trivial at first, but it affects how the class can evolve, how it handles validation, and how it behaves in a public API.
public class Thermostat { public double Temperature; // field } public class ThermostatWithProperty { public double Temperature { get; set; } // auto-property }
Both allow reading and writing Temperature, but the property version gives you control over access. That control is the core of the c# field vs property decision.
Fields: The Simplest Storage Mechanism
A field is a variable declared directly in a class or struct. It stores data and has no behavior. Fields can be public, private, protected, internal, or protected internal. They can also be marked readonly or const.
public class Point { public int X; public int Y; }
Fields are straightforward. They are memory locations that hold a value or a reference. When you read or write a field, the compiler emits direct memory access instructions. There is no method call, no interception, and no way to run custom logic.
Because of that simplicity, fields are often used for internal state that should not be exposed outside the class. A private field is the standard backing store for a property.
Properties: Encapsulated Accessors
A property is a member that provides a controlled way to read and write a value. It is compiled into a pair of methods: get_PropertyName and set_PropertyName. The C# syntax hides these methods, but they exist in the compiled IL.
public class Person { private string _name; public string Name { get { return _name; } set { _name = value; } } }
This explicit property gives you a place to add logic. For example, you can validate the incoming value, raise an event, or lazily compute the value on read. The caller sees only the property, so you can change the internal implementation without breaking the public contract.
Auto-Properties and Backing Fields
Auto-properties remove the boilerplate of declaring a separate backing field. The compiler generates a hidden field and the accessor methods automatically.
public class Product { public string Name { get; set; } public decimal Price { get; set; } }
Auto-properties are useful when you need a simple public surface with no additional logic. They are also the basis for object initializers and many data-binding scenarios.
The generated backing field is accessible only within the class through the property. You cannot reference it directly. If you later need validation or a computed value, you can replace the auto-property with an explicit property and a private field without changing the public API.
Validation and Side Effects in Properties
Properties are the natural place to enforce invariants. A setter can reject invalid values, normalize input, or trigger a change notification.
public class BankAccount { private decimal _balance; public decimal Balance { get { return _balance; } set { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Balance cannot be negative."); _balance = value; } } }
This validation would be impossible with a public field. Even if you use a field internally, you can expose a property that controls access. The property acts as a gatekeeper, ensuring that the object's state remains valid.
Properties also allow lazy initialization. A getter can compute a value on first access and cache it in a private field.
private List<Order> _orders; public List<Order> Orders => _orders ??= LoadOrders();
This pattern keeps the public interface simple while deferring expensive work until it is actually needed.
Performance: Field Access vs Property Access
A field access is a direct memory load or store. A property access is a method call, but the JIT compiler often inlines simple getters and setters. In practice, for a property that just returns or assigns a field, the generated machine code is identical to a field access.
The performance difference becomes noticeable only when the property contains complex logic, such as validation, computation, or locking. Even then, the overhead is usually negligible compared to the cost of the logic itself.
Do not choose a field over a property purely for performance. The JIT is effective at optimizing trivial properties. Instead, focus on the API design and maintainability. If you later need to add logic, converting a field to a property is a breaking change for any code that references the field directly.
Choosing Between a Field and a Property
The decision comes down to the public contract and the level of control you need.
| Criterion | Field | Property |
|---|---|---|
| Access control | No logic, direct access | Can enforce validation and side effects |
| API stability | Changing to property breaks callers | Can change implementation without breaking callers |
| Serialization | Some frameworks treat them differently | More widely supported by data binding and ORMs |
| Performance | Direct memory access | Potential method call, usually inlined |
Use a field when:
- The value is purely internal to the class and never exposed outside.
- You need a constant or a
readonlyfield that is set once in the constructor. - You are writing a
structwhere the semantics of direct access are intentional.
Use a property when:
- The value is part of the public API.
- You need to validate, compute, or intercept reads and writes.
- You want the flexibility to change the internal implementation later.
- You are using data binding, serialization, or reflection-based frameworks that expect properties.
For most public data members, a property is the safer choice. It gives you a stable contract and room to evolve.
Common Pitfalls: Mutable Structs and Readonly Fields
One subtle issue is exposing a mutable struct through a property. If a property returns a struct by value, the caller gets a copy. Modifying a field of that copy does not affect the original object.
public struct Vector { public int X; public int Y; } public class Transform { public Vector Position { get; set; } } var t = new Transform(); t.Position.X = 5; // Compiler error: cannot modify the return value
To modify a struct stored in a property, you must assign a whole new value. This is a common source of confusion. If you need to mutate the struct in place, consider exposing a field instead, but that breaks encapsulation.
Another pitfall is using readonly on a field that holds a mutable reference type. The readonly keyword prevents reassignment, but it does not make the object immutable. You can still call methods that change its state.
private readonly List<int> _items = new List<int>(); // _items.Add(1) is allowed; _items = new List<int>() is not.
Properties can also be readonly in the sense of having only a getter, but the underlying object can still be mutated. Keep this distinction in mind when designing immutable types.
Finally, remember that auto-properties cannot be readonly in the traditional sense. You can have a getter-only auto-property that is assigned in the constructor, but you cannot use the readonly keyword directly on it. For a true read-only field, use a readonly field with a private setter or a getter-only property depending on whether you need the public API to be a property.