Understanding Value Types And Reference Types In C# Using Classes, Structs And, Enums

Value types are faster than reference types to allocate memory. They are immutable and cannot be changed. But, the value inside them can be changed. There are many cases where value types are useful, and many cases where reference types are useful. Structs and Enums are value types. Classes are reference types. In C#, structs are very close to classes in terms of implementation. Values are stored in stack and classes are stored in heap.
 
One question that we need to think about is when to use classes and when to use structures or enums?
 
Structures are useful when creating lightweight objects. Enums must be used when we have a well-defined distinct set of named constants like days, months, etc.
 
Let’s start with reference types.
 
Let us take one class. This class is about the number that appears on dice, so it must be between 1 to 6. Also, one method that will be outside this class will help us understand references.
  1. public class MyDice {  
  2.     public int DiceNumber {  
  3.         get;  
  4.         set;  
  5.     }  
  6. }  
  7. public static void ClearNum(MyDice obj) {  
  8.     obj.DiceNumber = -1;  
  9. }  
Then, we will create one object and reference the second object to the  first object.
  1. MyDice object1 = new MyDice();  
  2. MyDice object2 = object1;  
  3. object1.DiceNumber = 1;  
  4. Console.WriteLine(object2.DiceNumber); //Output : 1  
  5. ClearNum(object2);  
  6. Console.WriteLine(object2.DiceNumber); //Output: -1  
Why? Because it is reference type!
 
Note
Arrays are also reference types.
 
Now, let us create a Value Type. There are two value types, Structs and Enums. The dice can be used as a structure. It can be allocated faster and can work without being used as a reference type. From the value types, we are going to take struct first.
  1. struct MyDice2 {  
  2.     public int DiceNumber;  
  3. }  
  4. public static void ClearNum(MyDice2 obj) {  
  5.     obj.DiceNumber = -1;  
  6. }  
We will perform the same operations as above. This will help us understand how value types work. This, we might be familiar. Let’s repeat the steps as above and compare with the above output.
  1. MyDice2 strObj = new MyDice2 {  
  2.     DiceNumber = 5  
  3. };  
  4. Console.WriteLine(strObj.DiceNumber); //Output : 5  
  5. ClearNum(strObj);  
  6. Console.WriteLine(strObj.DiceNumber); //Output: 5  
It didn’t work as expected like class because it is a value type.
 
In the above cases, in the real world, we need to perform a check if the number is between 1 and 6. Why do we need even a struct? We will use an enum as well as defined constants. Now, let’s replace struct with enum type,
  1. enum MyDiceEnum {  
  2.     ONE,  
  3.     TWO,  
  4.     THREE,  
  5.     FOUR,  
  6.     FIVE,  
  7.     SIX  
  8. }  
It's much more easy to understand and no additional validation required. Also, it is a value type. It cannot be simplified even more. It can be directly added in another class or enum.
 

Conclusion

 
To optimize the data type for memory usage we need to decide which type to use. Depending upon the application of any particular data type we can make use of struct and enum wherever possible. However, overutilization of data types can lead to performance degradation. We may or may not see major performance improvements. One major benefit of structures is there is no memory overhead. Anything more complex can be used with classes.