1. Auto-Properties
In C#, properties (property) are kinda like a mix between a field and a method. Basically, it's syntactic sugar that makes working with encapsulation super convenient. You use a property like it's just a regular field, but under the hood, there can be any logic: data validation, changing other fields, calling methods, or even sending an email to your grandma (the last one—not recommended). In the last lecture, we wrote properties by hand. But that gets old fast if you have 100500 simple objects where you just need a "getter" and "setter"—with no special logic.
Enter auto-properties. They'll save you from boilerplate code and let C# handle storing the value for you.
How it used to be: manual properties
When you want to hide a field but give access to it via a property, you usually write it like this:
public class Dog
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
}
This kind of code is classic "boilerplate". And most of the time, you don't need to "add" anything to those get/set! Just give access—and that's it.
How you can do it now
In C#, things got way easier: you don't have to write fields by hand anymore—the compiler does it for you. Just write:
public class Dog
{
public string Name { get; set; }
}
And that's it. You can read and change the Name value, and under the hood, the compiler creates a private field that you can't access directly from outside.
So what's really going on behind the scenes? When you write public string Name { get; set; } in your code, the compiler automatically creates a hidden field like Name__BackingField. Every time you access Name, it inserts the right getter or setter. The field itself isn't visible in your code, but it exists—and you can even find it with reflection (but that's a whole other story).
What's cool about this? First, it's way shorter and you don't have to repeat yourself. Second, your code is cleaner: less visual noise, easier to read and maintain. And finally, it's safer—no one can mess with your fields directly, access is strictly through properties.
2. Auto-Property Syntax
The syntax is super simple:
public class Dog
{
public string Name { get; set; }
public int Age { get; set; }
}
Now our Dog looks way more concise! We can create objects and set values like usual:
Dog myDog = new Dog();
myDog.Name = "Rex";
myDog.Age = 4;
In a nutshell
| Way | Syntax | Value Access |
|---|---|---|
| Field | |
Directly accessible |
| Manual property | |
Access via get/set |
| Auto-property | |
get/set + hidden field |
3. Customizing Access: Read-Only, Write-Only
Read-Only: get-only
If you only need to read a property (like a pet's registration number), you can make it read-only—just specify get, and either don't write set at all, or make it private. You can assign the value only in the constructor or right at the declaration:
public class Dog
{
public string RegistrationCode { get; }
public Dog()
{
RegistrationCode = "DOG-001"; // Assign value in constructor
}
}
Now from outside you can only read RegistrationCode, but not change it. Great way to protect important data from accidental changes.
Another option: private set
public string Name { get; private set; }
Then you can only change the property inside the class (like in a method or constructor).
Write-Only: not a great idea
Technically, you can make a set-only property (write-only), but that's super rare and not recommended—imagine an object that acts like a "black hole": you can throw stuff in, but never get it back.
4. init-only setters
A little backstory
Before, if you wanted to make an immutable object (immutable), you had to write get-only properties and set values via the constructor. OK, that's fine—but not always convenient. But recently, C# got a new syntax: init-only properties.
What's the deal with the init setter?
- Lets you set the property value only when creating the object (i.e. in the constructor or in the object initializer).
- After the object is created, the property becomes read-only.
- Perfect for "almost immutable" classes, DTOs, configs, models.
public class Dog
{
public string Name { get; init; }
public int Age { get; init; }
}
Now you can do this:
Dog dog = new Dog { Name = "Buddy", Age = 2 };
dog.Name = "Rex"; // Error! After creation—read-only
You can also set values in the constructor:
public Dog(string name, int age)
{
Name = name;
Age = age;
}
Chart: when can you assign
| Where to assign? | { get; set; } | { get; private set; } | { get; init; } |
|---|---|---|---|
| Outside class, after creation | ✅ | ❌ | ❌ |
| In constructor | ✅ | ✅ | ✅ |
| In initializer | ✅ | ❌ | ✅ |
| Inside class | ✅ | ✅ | ❌ (except constructor) |
5. Using in Practice
Let's rewrite our Dog class using auto-properties and init-only properties:
public class Dog
{
// Property, set only during initialization
public string Name { get; init; }
public int Age { get; init; }
// Auto-property for current state
public bool IsHungry { get; set; }
// Method, dog's actions
public void Bark()
{
Console.WriteLine($"{Name} says: Woof!");
}
}
Now we can create Dog like this:
Dog dog = new Dog { Name = "Spot", Age = 3, IsHungry = true };
dog.Bark(); // prints: Spot says: Woof!
dog.IsHungry = false; // you can change this property after creation
dog.Name = "Rex"; // Error! Property is init-only
6. Auto-Properties with Default Values
C# is all about convenience. You can even set a default value for a property right away:
public class Dog
{
public string Name { get; set; } = "Nameless";
public int Age { get; set; } = 0;
}
Or with init:
public class Dog
{
public string Name { get; init; } = "Puppy";
public int Age { get; init; } = 0;
}
Now if you don't specify Name when creating, it'll be "Puppy".
7. Auto-Properties with Different Access Levels
You can make a property readable and writable with different access modifiers:
public class Dog
{
public string Name { get; private set; }
public int Age { get; private set; }
public Dog(string name, int age)
{
Name = name;
Age = age;
}
}
In this case, you can only change the name and age inside the Dog class, like in methods or the constructor.
Manual vs Auto-Properties Comparison
| Approach | Writing Complexity | Control in set/get | Mutability after creation | Use in models |
|---|---|---|---|---|
| Manual property | Long | Full | Any | Where logic is needed |
| Auto-property | Short | None | Any or private | Almost everywhere |
| init-only auto | Short | None | Only at creation | DTOs, configs |
8. Common Mistakes with Properties
Mistake #1: trying to assign a value to a get-only property outside the constructor.
If a property is declared with only get, the compiler won't let you change its value anywhere else. You can only assign in the constructor or right at the declaration. Forget that—and you'll get a compile error.
Mistake #2: mixing up a field and a property.
When you replace a regular field with an auto-property, make sure the rest of your code doesn't access the field directly. Field and property are different things, and if you leave old references, the compiler will either throw an error or your program will act weird.
Mistake #3: private set without understanding the consequences.
If a property has a private set, but somewhere else in your code you still try to assign to it—you'll get an error. This happens a lot by accident, especially when copying someone else's code. Always check if set is available where you want to use it.
GO TO FULL VERSION