1. Introduction
Let's start with the problems that pop up when you use fields directly. If you declare a field as public, anyone can change it right away and from anywhere in your program:
public class Dog
{
public string Name;
}
Dog dog = new Dog();
dog.Name = ""; // O_o ... that's like "dog's name = empty string"!
A user can assign the Name field totally ridiculous values, even ones that make no sense for your domain: an empty string, a name that's way too long, or even null. It's like if someone was assembling your new IKEA wardrobe and you gave them full access to the woodworking factory.
Just a reminder, encapsulation is when an object controls its own data. We don't trust the internals to just anyone, but give out special "doors" — properties (Properties) — that let you access the field, but with the option to check, log, modify, or react to value changes.
2. Definition and Syntax
A property is a special class member that looks almost like a field, but under the hood it's actually a pair of special methods: a getter (get the value) and a setter (set the value). With a property, you can:
- Allow (or block) reading/writing data;
- Add validation or logic when accessing data;
- Hide the internal field or even store the value somewhere else.
A property is declared a lot like a field, just with curly braces and the get and set keywords inside.
[access_modifier] type PropertyName
{
get { ... }
set { ... }
}
Here's an example for our Dog class:
public class Dog
{
private string _name; // internal field (private!)
public string Name
{
get { return _name; } // "getter": get the name
set { _name = value; } // "setter": assign the name
}
}
Note: the underscore is usually used for private fields (_name). That's the C# style standard.
3. How a Property Works
A property is kind of like a "guard" standing between an object's internal data and the outside world. Example:
Dog dog = new Dog();
dog.Name = "Sharik";
Console.WriteLine(dog.Name);
Explanation:
- When the program hits dog.Name = "Sharik"; — that's when the property's set method is called, and you can add any checks you want there (like making sure the name isn't empty).
- At Console.WriteLine(dog.Name); the get method is called, which just returns the current value or, if you want, something calculated on the fly.
It looks like a regular field, but actually, everything's under control!
4. Why Properties Are "Best Practice"
Most of the time, we don't give direct access to an object's internal fields. Even if it seems like you don't need checks right now, getting into the habit of wrapping data in properties really saves you when the rules change.
Example of "validation" when assigning:
public class Dog
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("Dog's name can't be empty!");
}
_name = value;
}
}
}
Now this thing:
dog.Name = ""; // Will throw an exception!
… protects our object from absurd values.
5. Properties: Read-Only, Write-Only, and Regular
Sometimes you only want to allow viewing the value (like, a dog only has a birth year, you can't change it), or the other way around — only setting it (rare, but maybe you want to do something mysterious).
- Read-only: just write get, leave out set.
- Write-only: just write set, leave out get.
Examples:
public class Dog
{
private int _birthYear = 2018;
// Read-only
public int BirthYear
{
get { return _birthYear; }
}
// Write-only (super rare)
public string Secret
{
set { /* do something with value */ }
}
}
6. Properties vs Fields
| Field | Property | |
|---|---|---|
| Syntax | |
|
| Access | Direct | Via get/set |
| Validation | No | You can add it in set/get |
| Extensibility | No | You can change it anytime |
| IDE integration | Shows up as fields | Shows up as properties (important for frameworks) |
Illustration: How a Property Works
sequenceDiagram
participant User as Object User
participant Dog as Dog Object
participant Field as Private field _name
User->>Dog: dog.Name = "Ryzhik"
Dog->>Dog: set Name("Ryzhik")
Dog->>Field: _name = "Ryzhik"
User->>Dog: print(dog.Name)
Dog->>Dog: get Name()
Dog->>Field: reads _name
Dog->>User: returns "Ryzhik"
7. Common Mistakes and Gotchas
Let's see where you might run into traps here.
A common mistake is mixing up fields and properties, accidentally exposing private data:
public string name; // This is a field! It's visible everywhere, dangerous!
Better like this:
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
Static properties exist to store values shared by all objects. But use them carefully.
Fun fact: if a property only has a get and no constructor, you can't change it at all — that's called an immutable property and it's a big deal in modern design approaches (we'll get back to this in lectures about record and data immutability).
GO TO FULL VERSION