Clean and responsible coding’s always been something I long to write for. Finally, I could start and do proper research on C#’s beautiful language nature. I’ll write about C# properties in this very first post. Hopefully, after reading this, you will know when to use properties and when to avoid them.

C# Properties?

In my daily life, I code with Java 8 and you don’t have an idea how much I miss C# Properties.

There are four types of properties that I am aware of.

  • Read/Write Properties
  • Read Only Properties
  • Write Only Properties
  • Indexing Properties

If I skip some knowledge here, please let me know in the comments.

A property is a member that provides a flexible mechanism to read, write, or compute the value of a private field. 

https://docs.microsoft.com

Read/Write Properties

I believe that everyone who hit this post already knows what this is. Therefore, I’d rather give an example.

class Player {
    private Vector2 location;

    internal Vector2 Location
    {
        get { return location; }
        set { location = value; }
    }
}

In the code above, we have read/write access to the location field. Don’t ever use properties with a backing field when you simply wrap a field! C# has some syntactic sugar to accomplish the same task with a shorter code. For instance, check the code below;

class Player
{
    internal Vector2 Location 
    { 
        get; 
        set; 
    }
}

Auto Property

class Player
{
    private Vector2 location;

    internal Vector2 Location
    {
        get => location;
        set => location = value;
    }
}

Expression Body Definition

The one on the left is called Auto Property. It is not a property type, but it is just a convention brought by C#. As you can see, it is very concise. In addition to that, you don’t need a backing field. The one on the right is creating a property using Expression body definitions.

All of the code blocks above will be compiled into the same code. Well, which one to use then? I believe that everyone would go for Auto Property since it is shorter and more readable. I’d go for the same one, too. However, the real question here is that if we should use Read/Write Properties?

If possible, don’t use it! Why?

When read/write properties are used, other objects will not have any restriction to change the state of this object. However, we don’t really desire any state changes directly from the outside of an object. An object’s state should only be changed by the object’s own behavior.

Read Only Properties

As can be understood from the name, these properties can’t be set from outside of the object. They only contain a getter.

class Player {
    private Vector2 location;

    internal Vector2 Location
    {
        get { return location; }
    }
}
class Player
{
    internal Vector2 Location 
    { 
        get; 
    }
}
class Player
{
    private Vector2 location;

    internal Vector2 Location
        => location;
}

The code parts above do the same thing when it is compiled. So, if I am just going to give read access to the outside, I use the Read Only Auto Property (2nd one). In case I need to compute something in the getter, I go for the property with Expression Body definition (3rd one).

Read Only properties with Expression Body for computed properties

In case we compute a value on the fly, using read-only properties are quite ideal. For example, the code sample below is completely fine.

class Player : MonoBehaviour
{
    private readonly Vector3 velocity = new Vector2(5, 0);

    internal Vector2 Location => this.transform.position + velocity * 5;
}

Don’t use it for readonly fields!

If you are having a private readonly field, don’t use a read-only property to make this field accessible from the outside. I strongly believe that having internal (or public) readonly fields are totally fine. Because, after all the code pieces below does the same thing.

class Player
{
    private readonly Vector2 location;

    internal Vector2 Location => this.location;
}
class Player
{
    internal readonly Vector2 location;
}

In most of the cases — I think — you just need read only properties. Of course, there can be always exceptions and some other use cases. Please share with me in the comment section in case you would like to point out something.

The Responsible Developer’s Guide for Using Properties

It will not be a silver bullet, but I’d like to create a guide for you. You don’t have to follow this all the time, however, you can get benefit from it.

Start with the less accessible field!

Always start with a private readonly field.

Can this field stay read only?

If this field will be set in the constructor and will never be changed during the lifetime of the object, then leave it as private readonly.

If you need access this field from outside make it internal readonly.

Should you modify this field outside the constructor?

If yes, then make the field just private. If you need to access to this field from outside, make it internal readonly auto property.

Does the getter contain logic rather than wrapping a field?

If yes, you have two options.

If this logic can be done in one-line, then use the read only property with Expression Body definition.

If not, use regular read only property definition.

Do you feel a strong need to have a setter?

If yes, stop there and think. Why do you need to change the state of this object from outside instead of triggering a behavior?

If it is not behavior related, it must be data related. In that case, create a Data Transfer Object (DTO) and in this DTO, have the read/write auto property.

Then use this DTO for data related actions instead of changing the state of the object directly.

Keep In Mind

  • If you just need to wrap a field use an auto property, if you have a one line logic, use a property with expression body definition, else use regular property definition. See the ObservableInteger struct for an example.
  • In case you are having a property with a backing field, don’t access to this field using the property. Use directly the backing field to prevent overhead.
  • If you feel that mandating a property is necessary and good to have, you can also use interface properties. Then making a change for the other developer would be harder and make them think.
  • You can also have properties in abstract classes and they can be virtual. So, they can be refined in the child classes so you can have more control over them. This can be handy when the way you access is changed into something else such as cached, or lazy loaded.

Conclusion

One of my professors used to say that good object-oriented design is strongly related to caring about the “state” of the object. If necessary, we can expose the state to the outside of the object, but changing it directly shouldn’t be an option by another object. Only the object’s own behavior should change the state of that object.

We use properties in order to control what is exposed and not exposed to the outside of the class. It is a strong way to encapsulate or hide data. Even though the guidelines in this post is not a silver bullet, I believe that it is beneficial.

Hopefully, things are clear. There will two more properties I will cover in the next post (Write Only Properties and Indexing Properties). Drop me a comment below, in case you need further clarification.