Why I don’t like C# properties
Everything in the real world has properties, so why not C# objects? That must have been the thought of the C# language designers years ago. Properties have been part of the C# language for decades. The basic idea of C# properties are, well, to be able to define properties on objects. The most basic use is to use it as an auto property, where the compiler generates a hidden backing field:
class Picture {
public int Width { get; set; }
}
...
// Somewhere else
Picture nicePainting = ...
nicePainting.Width = 300;
A picture on the wall has a width and height, those are clearly properties of the picture. But what happens when I try to change them, like C# properties allow? Is the picture scaled, or cropped, or teared apart? In the real world, properties are read only.
A car engine also has properties, like the maximum power it can deliver. However, even though those properties are advertised when buying a car, and even though you want to know those beforehand, you actually do not use those properties when operating the car. No car that I know of has a knob that allows you to set the amount of horse power. Instead, you hit the gas pedal and the engine does its job. You control how far you press the gas pedal, not how much power the engine delivers. So instead of:
class Engine {
public int Power { get; set; }
public void SetPower(int amount) { ... }
}
// Usage
int amount = engine.MaxPower * throttlePercentage / 100;
engine.SetPower(amount);
You interact with the engine more like:
class Engine {
public void Throttle(int percentage) { ... }
}
// usage
engine.Throttle(throttlePercentage);
That is, the amount of power is left to the engine. All you can do is press the gas pedal more or less.
This is not just a cosmetic change. Translated to object oriented design, it means that you are interested in giving commands, not asking values. That is because object oriented design is about behavior, not about data. Properties are however data, so they kind of pollute the design. Responsibilities are described in terms of behavior, not data. So any time a class reads a property from another class, there is a big chance of mixing up responsibilities between classes. And mixing up responsibilities causes tight coupling. And tight coupling makes the code resistant to change, prone to bugs and hard to validate.
Take this class for example:
class Car {
public int TrunkSize { get; }
public Car(int trunkSize) {
TrunkSize = trunkSize;
}
}
It describes a Car
that has a trunk with given size. The trunk size is passed when creating a new instance, and it is also exposed as a read-only property. Now imagine that the Car
needs to be changed, to support car models that have no trunk. This involves changing all usages of the TrunkSize
property, and if the Car
class is exposed as public API function, it also causes a breaking change. A better approach would be to find out why other clients would want to know the trunk size of a car, because there may be some hidden function that the Car
class is missing.
This is also the reason to advocate the Hollywood principle: don’t call us, we call you. In other words: don’t ask for information, but have commands sent instead. So this principle also means to design classes with methods that act on the class’ responsibility, instead of exposing properties.
Properties also break encapsulation. After all, encapsulation is all about not exposing the internal data of an object. But properties do just that, especially in case of auto properties. This comes with annoying consequences, as it couples the outside world to the inner parts of a class. You cannot change a property since the outside world depends on it. You cannot remove it, rename it or update it in another way.
Not all is bad
All this reasoning might sound like properties are a bad thing, and they should be removed from the language. That is not always the case however. There are certain occasions where properties come in useful.
Most applications cannot have a pure object oriented design at their boundaries. When interacting with a database, network services, disk or user interface, data has to leave the system, or is entering the system. This means that a design based on pure behavior is not possible there. Those are the situations where properties shine. At those places, you need a mechanism that makes the data accessible to the objects in the model, and that is what properties can do for you.