Data Types
Value Types vs Reference Types
Whenever you need to create a type, first ask yourself a question “What you want and Why you want it?”.
If you could answer your question, you can decide between the type you want to use.
- If you want to store your data, use value types and when you want to create an instance of your type by defining the behavior, use reference types.
- Value types are not Polymorphic whereas, the Reference types can be.
- Value types are most efficient in terms of memory utilization over reference types and produce less heap fragmentation & garbage.
- If you want to pass values to a method implementation, decide what you want to do and based upon your requirement, decide between value types and reference types.
- Use of reference type variables actually change the original value but use of value type will create a copy of the original variable and pass across the method. Thus, it protects your original value from accidental changes.
Hence, decide before you start the implementation, else once implemented it will be very difficult to change them over your huge application.
General Tips
✅ DO use language keywords instead of BCL types (e.g.
int
,string
,float
instead ofInt32
,String
,Single
, etc) for both type references as well as method calls (e.g.int.Parse
instead ofInt32.Parse
).// Good private int _salary = 100; // Avoid private Int16 _salary = 100; private Int32 _salary=100;
✅ DO use nullable data types whenever required.
You typically use a nullable value type when you need to represent the undefined value of an underlying value type. For example, a Boolean, or bool, variable can only be either true or false.
However, in some applications a variable value can be undefined or missing. For example, a database field may contain true or false, or it may contain no value at all, that is, NULL. You can use the bool? type in that scenario.
✅ DO use
var
when the type is explicitly named on the right-hand side, typically due to either new or an explicit cast.// Correct var stream = new FileStream(...); var request = Factory.Create<HttpRequest>(); // For transient variables that are only passed directly to other methods var item = GetItem(); // Avoid FileStream stream = new FileStream(...); var stream = OpenStandardInput(); // Exceptions int index = 100; string timeSheet; bool isCompleted;
Similarly, target-typed new() can only be used when the type is explicitly named on the left-hand side, in a variable definition statement or a field definition statement.
e.g. FileStream stream = new(...);, but not stream = new(...); (where the type was specified on a previous line).
Why: removes clutter, particularly with complex generic types. Type is easily detected with Visual Studio tooltips.
✅ DO prefer is and as operators while casting
It is better to use is and as operator while casting. Instead of Explicit casting, use the Implicit casting.
// this line may throw Exception if it is unable to downcast from Person to Employee var employee = (Employee) person;
In the above code, suppose your person is a Customer type and when you are converting it to Employee type, it will throw Exception and in that case, you have to handle it using try{} catch{} block. Let’s convert the same using is and as operators. See the below code:
// check if the person is Employee type if(person is Employee) (1) { // convert person to Employee type employee = person as Employee; (2) } // check if the person is Customer type else if(person is Customer) { // convert person to Customer type customer = person as Customer; (3) }
1 | Checking whether the person is a Employee type. |
---|---|
2 | If it is of type Employee, it will go into this block.Now, convert it with the as operator. Here, if it is unable to convert, it will return as null but will not throw any exception.So, in the next line, you can check whether the converted value is null. Based on that, you can do what you want. |
3 | Else if it is a Customer type, will go to this block. |