While this article will have some controversial points let's start from the point that we can all agree on.

1. Never use old-style, non-auto implemented properties unless there is a specific need to:

public string name { get; set; }

Should be preferred over:

private string _surname;
public string surname {
    get {
       return _surname;
    set {
       _surname = value;

The former has been the standard since C# 3.0. The later also increases the line count by 7, more or less depending on your coding convention.The problem here is that the code becomes heavy with boilerplate even before any operations have been introduced. Scrolling through a class with forced, useless properties quickly becomes a chore, not to mention the wasted time and writing effort, especially while iterating on the code. Programming is not typing, and having more verbose code is actually worse than having clear and terse code. In modern programming languages lots of effort is dedicated to eliminating this kind of useless ceremony and making programmers faster.

Consider clear drawbacks of properties:

  • Properties are not serialized in Unity. This means they won't appear in the Inspector and won't be saved as state in any of ScriptableObjects or similar classes.
  • You pay for a method call cost when accessing a property. Even though C# team (especially through posts from Eric Lippert) would have us believe this is an implementation detail and that C# programmers should not be worried about performance on this level, we are game developers and the cost of a method call is not negligible (as seen on this blog, you can also see the numbers, also some examples on Unity blog). The promise of the compiler was to inline this method, but this hasn't been done until very recently in .NET and still isn't done in Unity to my knowledge.
  • Nothing prevents us from changing the backing field of non-auto properties by accident. If we change the backing field by accident privately we will prevent the side effects and create a bug.

Now let's consider some clear benefits of properties:

  • Allow for better (proper) access modifiers. For example, we can have: public string name { get; private set; } and public string name { get; protected set; } This is clearly useful in 2 cases: (a) when we want to have some value publically observable, but have the ability to change it outside the constructor of the current class (if the value is changed only inside the constructor, it's another story as we'll see). (b) Allow for public gettable, protected settable combination; or a more rare combination protected-private. These allow you higher granularity in access modifiers then you'd get with fields.
  • Properties are used in data-binding systems. Unity is not such a system though.
  • In external (libraries) code, they allow internal changes without influencing binary compatibility (explained below). It is a standard, expected good practice.
  • They allow easy implementations of lazy loading and backing field caching.

Let's consider some arguable benefits of properties, purely for code written in Unity, in one assembly:

  • In general, IF we already have a property p and IF we decide to change the internals of p AND we are an author of a library that other programmers are relying on AND this library is not shipped with their project, THEN when we make the change the other code that relies on our library does not have to recompile (binary compatibility). This is in contrast to having fields in the first place, where changing a field into a property always breaks binary compatibility. However, in Unity we are making end products with all libraries contained within.
  • Changing the field into a property can be a bug-inducing change, in cases related to mutable structs and value semantics. These cases are a bad practice in itself and are bug waiting to happen with or without properties. It doesn't mean the bug wouldn't happen if we had the property in the first place, it means that if the field changes to a property the bug might happen and other code needs to be made aware of this change (code compatibility). The change can also be potentially breaking for other code that relies on our code. Outside Unity, since we know that this can potentially be a breaking change, then as a rule changing a field into a property with the same name is not a good idea when the backing fields are structs. In such cases a property with a different name should be created to avoid all the fuss, since clients will have to recompile anyway.
  • Systems that rely on reflection that expect a field will not work when it's converted to property with the same name. However, for such undesirable systems even a rename is a breaking change.
  • It allows debugging on value set, regardless where the value is coming from (for example, setting a breakpoint on a setter). However, we don't need to have all values be properties all the time just to have this. In case we ever wanted it, we can very quickly change a field to a property and use this little trick only when needed, provided we are not constrained by limitations above.
  • Allows introduction of logging on getters and setters, without introducing problems from above. However, logging is a non-trivial operation, which involves an external system (logging which writes to a file) and as such is poorly suited for a property, whose purpose is to "do little work". In such cases a method would be a better conceptual fit.
  • Conceptual fitness: properties promise something with a name and a type, regardless of how it's implemented. This creates an "interface" of a class of how it looks to the outside, and doesn't influence internal implementation of a class. My opinion on this is it really doesn't matter in internal code: field or property, from user's point of view as far as their code is concerned they behave just the same (if both could be used in such a case). Users caring if something is a field or property is a user's attempt to violate encapsulation.

Finally, let's consider some arguable drawbacks of properties (my opinion), again purely considered in internal code and Unity:

  • When a field is only set in a constructor, and we want to make it publically accessible, it is better to make it public readonly field then public-private property. This allows an additional level of safety from internal change, and also makes that field thread-safe for free, avoiding the need for synchronization on that field in multi-threaded systems.
  • Auto properties do not improve encapsulation in internal code, they have the potential to improve the encapsulation if the internals of a property ever need to change without changing the "interface" of a class. However, in practice, the need to introduce side-effects on existing fields is very rare. Even if the need to change exists, it's very easy to do it with modern refactoring tools regardless of the size of the codebase in just a couple of clicks.
  • They can do more work than expected. When we access a property, we are expecting something that behaves just like a field. However, during debugging, it can be confusing to realize we are actually dealing with something that has side-effects, and now have to trace them.
  • They encourage local (class) thinking instead of systems thinking. The former is a standard in OOP logic, where the unit of encapsulation is a class, an idea that has always remained unchallenged. This idea has come from the desire to minimize the amount of change and localize it to a single point, which is perfectly reasonable. I have recently moved away from this thinking and started experimenting with a more systems thinking mindset when designing the classes. The idea is that a class usually doesn't do anything by itself; it is a piece of a system that interacts with other classes to form a system. Such a system is defined by flow of data and interactions between objects. One class can also be a system, but a system can span multiple classes. In that sense, encapsulation is important on the boundaries of a system: we don't want dependants of a system to have to change when the internals of a system change. An assembly is surely a system: it has a set of classes that are isolated from the outside, but it probably also contains many systems that are separate within it. In functional logic, every method can be a system, since it is isolated from everything else and has no side effects. If 2 classes depend on one another (a situation that is to be avoided) then they are surely a part of a system, but there are other indicators. So, if we want to change a class, should we always hide that change from other classes surrounding it? If we always did that, then the interface of classes within a system would quickly become legacy. The idea is that a piece of a system is always separate from the whole system creates all kinds of inefficiencies on the systems level to spare us from changing the elements of that system. OOP purists may say that a class is already a system in itself, and that classes should be designed like separate systems. This is actually not true, since classes are designed like entities, which doesn't make them end-to-end systems in themselves. This is especially true in Component model, where every component is a class, but components frequently have to communicate with one another to complete tasks. This is thinking on more of an end-to-end level, rather than operation level.

Reference: http://csharpindepth.com/Articles/Chapter8/PropertiesMatter.aspx

If you're finding this article helpful, consider our asset Dialogical on the Unity Asset store for your game dialogues.