Over the last few years, Helix design principles have become the community standard and dramatically improved overall quality and maintainability of Sitecore solutions. I believe even small enhancements are beneficial, therefore in this blog post I will share some details that should help you decide how to store Sitecore template references in your project.
You have probably seen that in Habitat GUIDs of Sitecore templates and fields are stored in structures. And Helix documentation also refers to structure as the only construct of the common type system to hold references (IDs) to templates and fields. Here I will describe what considerations you should be aware of when deciding to use structures to hold Sitecore template GUIDs and why static classes can be more suitable for such purpose. I will also share some memory usage aspects for both of the cases.
According to Helix principles, constants for a module’s templates should be defined in a single type, which is located in the module’s root namespace. It should never contain references to templates that are not part of the module itself. These aspects make it easier to discover template references.
What type in the CTS is more suitable to hold Sitecore template references? Here are the considerations favoring static classes over structures:
1. It is possible to instantiate a structure, which can be confusing to allow.
When designing types, methods, we think about how others could use them. It is important to consider what can go wrong and make it clear how to use them. One of the caveats when using structures to hold template references is that we allow to instantiate a structure, which is not intended and is an undesirable behavior.
On the other hand, if you try to create an instance of a static class, the compiler will generate CS0712 error.
2. Microsoft recommends using static classes to define constants
Currently, the Helix documentation shows that the conventions define to choose only structs to clearly signal the Templates type’s unique function as a constants holder only. I believe it is, indeed, very important to indicate that, though a static class can fit here even better. In Microsoft’s programming guide on how to define constants they actually suggest to use static classes. For even further description of it’s purpose, class name qualifier can be made very self-descriptive, for example “TemplateConstants”.
Furthermore, it can also be controversial that structures are used to hold constants only, according to this discussion on SO. In some other programming languages it could be true (like C/C++), but in C#, structures can have properties, methods and even implement interfaces.
On a side note, when using a structure to hold template references, one of the ways to make the intention more clear is to define it as readonly. It will enforce immutability and will protect the values from unintended modifications. Public members and the "this"
parameter will be readonly. This feature was introduced only in C# 7.2.
3. Memory usage, size and copying aspects
As long as the Templates struct is used in a way it was designed to be used memory and copying aspects of how structures behave should not be a concern. Though, it is worth noting the cases when it is recommended to avoid defining a structure.
As a value type, if an instance of a structure will be passed to a method, a new copy will be created. And if an instance size is not small, this can negatively impact the performance.
If you assign a structure to another structure, a field-by-field copy is made, which can be expensive when a structure gets big. Take one of the Template structures in Habitat as an example. It contains reference types (Sitecore.Data.ID). In this situation, assignment results in a copy of the references. There are two independent struct objects, each of which contains a reference pointing to the same object in memory. It is a shallow copy. Even though it is very unlikely that someone would decide to assign the Templates structure to a variable (or to pass it to a method), still it does not seem to be prudent not to adhere to Microsoft’s recommendation to avoid defining a structure when it has an instance size more than 16 bytes. In our example, the structure contains only reference types and field-by-field copy is made. Considering that one object reference is 8 bytes in x64, usually, this recommendation is not met.
Let’s look into more details of how Sitecore template GUIDs will be kept in memory if defined inside a structure as static variables, as it is in Habitat.
The runtime creates one global instance of each static variable as soon as the code referencing them is loaded and used. They can be accessed by all threads in an application domain.
It depends on the situation and the implementation, but, usually, an instance of a structure will be allocated on the stack (except, for example, when it is part of a class, etc.). Static variables, like static fields that we declare to hold GUIDs of Sitecore templates, are always stored on the heap, regardless of whether was it declared in a value type or a reference type. They will hold references to objects in the heap, not the object itself. Static variables are stored in the heap known as high frequency heap and it is not garbage collected. There is one per application domain. The code shown on the image below is part of the Templates struct in Habitat, Sitecore.Feature.Demo project. The image illustrates how template and field GUIDs are stored in memory.
Figure 1. Static variables are stored on the High Frequency Heap and contain references to instances of Sitecore item ID, stored on the heap.
If you would want to see even more in-depth details of memory usage, you can debug the application with WinDbg and SOS.dll (SOS debugging extension). It will also show details about high frequency heap, GC generations and other. To load SOS.dll, you can run the following command:
.loadby sos clr
And in order to see information about process memory consumed by internal CLR data structures, the next command should be executed:
!eeheap [-gc] [-loader]
Figure 2. WinDbg/SOS.dll displays information about process memory consumed by internal CLR data structures.
To finalize this point: Microsoft’s recommendation to avoid defining a structure when it has an instance size more than 16 bytes can be disregarded in usual use case scenarios when the Templates structure is not passed to a method, assigned to a variable or frequently boxed/unboxed. Nevertheless, there are a few unintended cases when this structure can be used wrongly and will turn out to be costly in terms of performance. Therefore, from this standpoint, it would still be logical to follow the recommendation and use static classes instead.
TL;DR
In conclusion, based on the aforementioned details, a static class seems to be a better choice to hold Sitecore template constants, as Microsoft recommends defining constants in a static class and it also helps to avoid possible complications that could occur if the Templates structure will be used in a wrong way (passing to a method, assigning to a variable, creating a new instance). If you think differently, or would like to add something, please leave a comment below. I’ve also opened an issue in Habitat repository on GitHub and your comment is very welcome there.
Useful links: