Clean and Responsible Coding in C# with Unity: Properties II
Welcome to the second post on C#’s Properties. If you didn’t read the first one, I strongly recommend the read it before reading this one. On that post, I have tried to give you my best practices on Read-Only and Read/Write properties. In this post, you will read about;
- Write-Only Properties
- Indexing Properties
- Inheritance of Properties
Write-Only Properties
Honestly, I am not aware of any use cases of this type of property. I think the write-only property is a side effect of the whole properties feature of C#. If someone designed the read/write property and the read-only property, that person will have write-only property out of the box as well, right? I can’t think of any other reason for allowing to alter the state of an object, but not able to read it. If you are using it, it surely a bad design.
Let me give an example;
class Player { internal Vector2 Location { private get; set; } }
You see write-only auto-property above. Here, we allow outside objects to change Player object. However, they can’t see the Location from outside. They can only change. Does this make any sense?
Well, let’s say that you started with private property and you have multiple players. There is also a class called PlayerMovementManager (Bad idea!). It makes the Players move, changes their Locations. Even in this scenario, what you want is not to use the write-only property for Location. What you want is creating a behavior by creating a method named MoveTo(Location location). The state of an object should be changed by the behaviors.
Indexing Properties
Sometimes giving an example first is better than trying to describe it. Have you ever used a dictionary in C#? If yes, you can relate immediately. While we try to retrieve a value from a dictionary we actually use indexing property (also called indexer) of C#. For instance, players[“Mark”] or cars[“Mercedes”] are using indexers as [key]. If you are familiar with Java, you must know that this is not possible there since there is no indexer feature. In Java, you’d have to use players.get(“Mark”).
How to use it?
Let’s take a look at the example below.
internal struct RowOrderMatrix { private readonly int width; private readonly int[] array; internal int this[int row, int col] { get => array[this.width * row + col]; set => array[this.width * row + col] = value; } internal RowOrderMatrix(int width, int height) { this.width = width; this.array = new int[width * height]; } }
Here I have tried to create a valid use case for you. Let’s say that I want to use a 1D array mapped to a row order matrix for the sake of efficient cache usage. In addition to that, I would like to hide the complexity. Then, using an indexer is a perfect match for this use case. The usage of the indexing property is given in between lines 6 and 10. You need to use simply this keyword to define an indexer. You already know the story behind the get and set accessors. The usage would be like;
{ RowOrderMatrix grid = new RowOrderMatrix(5, 6); grid[2, 4] = 24; Debug.Log($"The value I have just set = {grid[2, 4]}"); }
I think, this example enough to show that we hide the implementation complexity. Usually, I am not a big fan of hiding, but in this case, it makes more sense since the hidden complexity is not important. Thanks to indexers, we can use this RowOrderMatrix as a 2D array without knowing its implementation details. If we didn’t have indexers, we could still do it with using getter and setter. The indexer is more like a syntactic sugar here.
When to use it?
Here, I will list some of the valid use cases;
- To create a more restricted collection
- an immutable collection of days of the week.
- an immutable collection of month names.
- To create a custom collection and give access like a native collection
- a custom dictionary or a more capable list
- to create a data structure backed by collection like objects
- a row or column order matrix
- a grid which accepts vector class instead of x and y indices
- a game board grid that is indexable by coordinates…
The list can go further, I guess you already get the point.
You can use this property wrong in a bunch of cases. I can’t really list the bad usages of it, because if you are using this property wrong, it is probably the structure you have created that is already unnecessary. You should always try to keep things as simple as possible.
Inheritance of Properties
Most developers look at the properties as something can only exist in the current class. Sometimes I do the same. However, if you are designing a more complicated system, the inheritance of properties is also possible.
Whatever you can do with regular methods, you can do it for the properties as well. You can have them in the interface, you can define them as virtual. The same rules are applied for the virtual keyword. I mean a virtual property can be overridden in a derived class by using the override modifier.
Interface Properties
As I stated above, we can use properties in the interface definition. Here is a quick example;
{ public interface CommonItemDto { // Property declaration: string Name { get; set; } } }
.. and the implementation of this interface would be as defining a regular property.
{ public class BaseItemDto : CommonItemDto { public string Name { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } } }
This can be really useful. Because, as you may know, we should never rely on concrete implementations. This is also true for data carrier objects. The objects you don’t have any functionality. They serve only data. In other words, there have only properties. In some cases, you can also build an inheritance mechanism between your data classes.
For example; you will have hundreds of items in your game. These items have different effects on the player object. You can actually share a great deal of code between those item classes. While you are sharing code among your item classes, you can also have properties in the interfaces. Then you can only rely interfaces where possible. This would make your classes much more robust and strong to future changes.
Conclusion: C# properties
I am so glad that I have completed the properties. This has been a great research for me. I have also learned some stuff I didn’t know before. You guys are lucky to have at least a piece of complete information and some of the best practices about properties. I hope you enjoyed it because I am planning to continue to write on the C# language.
It is also worth to share what Microsoft thinks as good and bad for properties. I have to state that there are some conflicting ideas with this post, that’s simply because I have different ideas and saw them working well.
Let me know please if you have any counter ideas and discuss them in the comments section.
Great Post, thanks for sharing