These are a few things I learned over many hours of work, conversations with coworkers, and “a-ha” moments. They are not listed in any particular order. And feel free to share them with others; if you are unfamiliar with some or even all of them, don’t feel bad about it. I didn’t know them for years in some cases. Thus, if you accept them, you might become a better .NET developer.
1. Out parameter always changes value
Here is some simple code and a simple question:
int testParameter = -1; Int32.TryParse(“abc”, out testParameter);
What will the value of testParameter be after the TryParse executes?
It is simple to test for yourself; simply enter this into a console application and see the testParameter value displayed. You are incorrect if you thought it would be -1. The value of the int will become the default value, which will be 0 (zero), even if the TryParse fails. According to MSDN, the out parameter is as follows: This parameter is passed without initialization, overwriting any value that was initially entered in the result.
2. Enums can have extension methods
A large Enum containing a variable’s possible values was present in the code. I won’t go into why we need it or if it was the right decision for us because those things aren’t relevant to this situation. Let’s imagine it looked like this (I promise it was larger; this is just a scaled-down version of an example that is similar):
enum Day {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};
Now picture your code looking something like this if you wanted to check if it was the weekend:
if (dayParameter == Day.Sun || dayParameter == Day.Sat) ….
IsItWeekend(dayParameter) is the method I came up with. A colleague then suggested, “Instead of having the above method in every class we might need it in, why don’t we create an extension method?”
To be completely honest, I didn’t think it would be possible to create an extension method on Enums, but after two days, I apologized to him and told him that it was in fact possible. And I discovered that it’s incredibly strong because the most popular workaround is to create a Helper class, which I detest. Since there is a fantastic article on MSDN about how to create such an extension method, I won’t go into details here. It is, in my opinion, more crucial to keep in mind that it is possible.
3. Use HashSet for distinct items
I find this code quite a lot in many projects:
List<string> uniqueItems = new List<string>();if(!uniqueItems.Contains(parameter)){uniqueItems.Add(parameter);}
Is there an issue with this then? It functions, but Contains searches the list linearly, which takes O(n) time. which, if you end up with a lot of them, could cause performance problems in production (usually big CPU usage). Prior to.NET 3.0, Dictionaries were being created with values set to NULL and the key collection being used for distinct lists. However, as of.NET Framework 3.5, HashSet is a brand-new class. There are no duplicate elements in this collection, and the contains method is an O(1) operation. Thus, our code quickly turns into:
HashSet<string> uniqueItems = new HashSet<string>();if(!uniqueItems.Contains(parameter)){uniqueItems.Add(parameter);}
Furthermore, since Add only returns false (or true if the element will be added) and doesn’t add it if it already exists, we can do away with the Contains call. Thus, once more, the code looks like this:
HashSet<string> uniqueItems = new HashSet<string>();uniqueItems.Add(parameter);
4. Many value types don’t live on the Stack
Reference types are assigned to the Heap and value types to the Stack. We say that in interviews and we get away with it because that’s what we learned. We started with C# since this is probably what many introductory.NET books say, so we read it from there. However, that is untrue and merely a myth; there are a lot of value types that are saved on the heap, and these are two significant instances of when that occurs.
- The value type is saved on the Heap if it is an instance variable of an object. Simple: value types are allocated there as well, since that is where the entire object resides.
- Static variables present an additional scenario wherein a value-type static variable is allocated on the Heap. Additionally, the fact that that variable only exists in one instance makes sense.
You may wonder, then, what distinguishes value types from reference types. That’s very straightforward: reference types are passed by reference, whereas value types are passed by value (even if it’s an instance variable of a reference type). which implies you can respond to a question in an interview more accurately the next time.
5. Response.Redirect on out of process sessions
One of the reasons this is my favorite is that I discovered it in 2014 when we wanted to use an out-of-process session for an existing application. Our goal was to support multiple servers, but since we were deploying on ASPHostPortal, we didn’t want to use sticky sessions because doing so would have eliminated all of the benefits of auto scaling. Using Response and going outside of the process during the session did teach us a lot.Redirect was among the results. Assume we have the following basic code:
Session[“FlowId”] = Guid.NewGuid();//more code goes hereResponse.Redirect(Url.Action(“Account”, “Login”));
While the session is running, this functions flawlessly; however, the Session[“FlowId”] item will not be saved when the session ends. It takes place as a result of Response.The thread never reaches the stage where it saves the Session object in the external store because redirect raises a ThreadAbort exception. Throwing an exception has no impact on saving when the Session is used in process because all changes are made instantly.
What then is the key to making it function? Easy, Reaction.The redirect overload accepts a second parameter, endResponse; if you pass it false, the exception won’t be raised. For performance reasons, setting it to false is even advised.
Session[“FlowId”] = Guid.NewGuid();//more code goes hereResponse.Redirect(Url.Action(“Account”, “Login”), false);
6. A generic type creates different classes
We have the following class:
public class GenericCache<T>{public static int ctorCalls = 0;private List<T> list;public GenericCache(){ctorCalls++;list = new List<T>();}}
For educational purposes only, the implementation is pretty stupid, so don’t ask me why ctorCalls is a public field. However, a large application will often contain such a class. Because you will use the Cache class for many of your classes, it will eventually become generic. Until then, it must always exist in order to save costly I/O calls.
GenericCache<string> genericCache = new GenericCache<string>();GenericCache<int> genericCacheInt = new GenericCache<int>();Console.WriteLine(GenericCache<DateTime>.ctorCalls);
The generic Cache class’s constructor is called twice in the lines above, and we want to print the value of ctorCalls to the console after that.
What value will it show, then? You are incorrect if you answer “two,” though. GenericCache differs from GenericCache and GenericCache in terms of type (class) behind the scenes. This is what generic does. Consequently, there will be three instances of the static variable ctorCalls—one for each generic type. Consequently, the console will show 0 as the value.
7. Order of logical operations (AND and OR)
Concluding with a simple yet effective one. If this is not handled correctly, it could give you a lot of headaches: || (OR) lacks the power of && (AND). To understand what that means and how it might impact your code, let’s look at a few examples:
bool result; result = (true || true) && false; //this gives false result = true || (true && false); //this gives true result = true || true && false;// what will be the result of this?
L̵i̵k̵e̵ ̵I̵ ̵s̵a̵i̵d̵ ̵a̵b̵o̵v̵e̵ ̵A̵N̵D̵ ̵i̵s̵ ̵m̵o̵r̵e̵ ̵p̵o̵w̵e̵r̵f̵u̵l̵ ̵t̵h̵a̵n̵ ̵O̵R̵,̵ ̵w̵h̵i̵c̵h̵ ̵m̵e̵a̵n̵s̵ ̵i̵t̵ ̵w̵i̵l̵l̵ ̵b̵e̵ ̵r̵u̵n̵ ̵f̵i̵r̵s̵t̵,̵ ̵e̵v̵e̵n̵ ̵i̵f̵ ̵i̵t̵ ̵i̵s̵ ̵t̵h̵e̵ ̵s̵e̵c̵o̵n̵d̵ ̵o̵p̵e̵r̵a̵t̵i̵o̵n̵.̵ ̵S̵o̵ ̵t̵h̵e̵ ̵t̵h̵i̵r̵d̵ ̵c̵a̵s̵e̵ ̵a̵f̵t̵e̵r̵ ̵w̵e̵ ̵d̵o̵ ̵t̵h̵e̵ ̵A̵N̵D̵ ̵o̵p̵e̵r̵a̵t̵i̵o̵n̵ ̵w̵i̵l̵l̵ ̵b̵e̵c̵o̵m̵e̵:̵ ̵r̵e̵s̵u̵l̵t̵ ̵=̵ ̵t̵r̵u̵e̵ ̵|̵|̵ ̵f̵a̵l̵s̵e̵ ̵s̵o̵ ̵i̵t̵ ̵w̵i̵l̵l̵ ̵g̵i̵v̵e̵ ̵y̵o̵u̵ ̵t̵r̵u̵e̵.̵ ̵S̵o̵ ̵i̵t̵ ̵i̵s̵ ̵e̵x̵a̵c̵t̵l̵y̵ ̵t̵h̵e̵ ̵s̵a̵m̵e̵ ̵w̵i̵t̵h̵ ̵t̵h̵e̵ ̵s̵e̵c̵o̵n̵d̵ ̵o̵p̵e̵r̵a̵t̵i̵o̵n̵.̵ ̵N̵o̵w̵ ̵m̵y̵ ̵s̵u̵g̵g̵e̵s̵t̵i̵o̵n̵ ̵i̵s̵ ̵t̵o̵ ̵a̵l̵w̵a̵y̵s̵ ̵u̵s̵e̵ ̵p̵a̵r̵e̵n̵t̵h̵e̵s̵i̵s̵ ̵f̵o̵r̵ ̵t̵h̵e̵ ̵o̵r̵d̵e̵r̵ ̵o̵f̵ ̵o̵p̵e̵r̵a̵t̵i̵o̵n̵s̵ ̵t̵o̵ ̵l̵o̵o̵k̵ ̵m̵o̵r̵e̵ ̵c̵l̵e̵a̵r̵.̵
The explanation above is incorrect; a short circuit is applied, not operator precedence, which is why the outcome will be true. Since it is aware that the first value is true, it simply stops and returns true rather than executing the || and && operators.
I’ll just post the link to the C# operators that state that the AND conditional parameter is above the OR here until I find a better example.
Conclusion
That’s all, and I sincerely hope it was useful. If you are aware of any more intriguing details that ought to be included in the list of pitfalls for.NET/C# code, kindly leave a comment.