Properties and Fields
Why C# favors properties over public fields, and how auto-properties keep the syntax lightweight.
2 min read
Fields hold data directly. Properties wrap access to data behind a getter and setter, which is what lets you add validation or logic later without breaking every caller.
The problem with public fields
class User
{
public int Age; // anyone can set this to anything, including -5
}
var user = new User();
user.Age = -5; // compiles fine, makes no senseNothing stops invalid data here. If you later want to add a rule ("age can't be negative"), you'd have to convert the field to something else — which, if Age is a public field other code already reads and writes directly, is a breaking change to your public API.
Properties: get and set
class User
{
private int _age;
public int Age
{
get { return _age; }
set
{
if (value < 0)
throw new ArgumentException("Age can't be negative");
_age = value;
}
}
}value is an implicit keyword inside a set block, referring to whatever was assigned. Callers still write user.Age = 30; — the syntax at the call site is identical to a field — but now validation runs on every assignment. This is why C# convention is to always expose properties, not public fields, from a class: it costs nothing today and gives you room to add rules later without breaking anyone.
Auto-implemented properties
Writing out a full backing field for every simple property gets tedious, so C# offers a shorthand when there's no extra logic:
class User
{
public string Name { get; set; }
public int Age { get; set; }
}This compiles to the same shape as a full property with a private backing field — you get the field-safety benefits with none of the boilerplate.
Read-only and init-only properties
Restrict how a property can be set:
class User
{
public string Name { get; } // settable only in the constructor
public int Age { get; init; } // settable in the constructor or an object initializer, never after
public User(string name)
{
Name = name;
}
}
var user = new User("Ada") { Age = 30 }; // init works here
user.Age = 31; // compile error: init-only, can't change after construction{ get; } with no setter can only be assigned inside the constructor. { get; init; } (from C# 9) is more flexible — it also allows the object-initializer syntax shown above — but locks the property after that point, which is a good default for data that should be treated as immutable once created.
Default to properties over public fields for anything exposed outside the class. The tiny bit of extra syntax buys you a lot of future flexibility.
Test what you just learned
4 quick questions. Get all of them right to unlock the next lesson.
You can take the quiz without an account — logging in just lets your result count toward your progress.