In this article I will show you some interesting C# things that I found out during the last 10+ years as a .Net/C# developer. Some of them triggered some „aha moments” both for me and/or my team.
Before I begin I need to mention that from my point of view Microsoft kept its promise to have the C# language clear to read, easy to understand and write thus making it a good language to start programming.
Without further ado, the following 10 nice to know and use language (and not only) features, in no specific order, are:
1. Launching the debugger;
One of the main things programmers do is debugging source code (the first thing is searching stackoverflow, of course). If the VS solution is big or monstrously big, F5 might work kind of slowly.
Another solution to debug is to attach to the relevant process (usually w3wp.exe) by using „Attach to process” window. This one is fast if you know the keyboard shortcuts, otherwise it takes some time. Also, for example if you have a web project and you want to debug the custom code you wrote for app initialization using this method might not give you enough time for the breakpoint to be hit.
The easiest way is to use System.Diagnostics.Debugger.Launch() which will trigger the „Choose Juts-In-Time Debugger” window. The downside is that you’ll need to remember to remove this line of code.
2. C# has destructors;
In fact they are called finalizers and are used to destruct instances of classes. Most likely you haven’t seen such constructs in C# code but the language supports it. Few finalizers characteristics are:
- Having a class called „Communicator” its finalizer method name would be ~Communicator()
- Finalizers cannot be called. They are invoked automatically when GC decides so you have no control.
- Finalizers cannot be defined in structs. They are only used with classes.
- Finalizers cannot be inherited or overloaded.
- A finalizer does not take modifiers or have parameters.
And the most interesting fact about finalizers is that they might never be run.
3. Conditional(„DEBUG”);
Sometimes you might need to have some code, like writing logs to Console, running only when the program is run in debug configuration.
#if DEBUG
public void LogToConsole() { }
#endif
public void ProcessData()
{
#if DEBUG
LogToConsole();
#endif
}
Though it works this way pollutes your code with debugging related commands.
The alternative is [Conditional(„DEBUG”)] method decorator. The above code will look like this:
#[Conditional("DEBUG")]
public void LogToConsole() { }
public void ProcessData()
{
LogToConsole();
}
It looks nice, clean, and easy to read. The differences is that in the first approach the debug code will not end-up into release build, it being removed during build time.
4. Parallel.For/ForEach;
.NET has several ways for you to write asynchronous code and several of them are provided by the Task Parallel Library (TPL). Among a lot of useful features, it includes two loop commands that are parallel versions of the for and foreach looping structures of C#. They provide the developers a easy way to write code that can be run in a concurrent manner.
Below you can find a short snippet on how it looks like:
Parallel.For(0, a.Length-1, i =>
{
a[i] = a[i] + a[i];
});
Use this especially for long running operations because this approach might be slower due to the overhead in setting up the parallel loop. For the above code, the sequential corresponding code is faster by 0.03 when then length of the array is 1000. When instead of calculating the sum of the array we download the same page, the elapsed total seconds is 85 seconds less.
Another option is Parallel.Invoke(params Action[] actions)
that offers the possibility to invoke an array of actions.
5. String.ToUpperInvariant rather than String.ToLowerInvariant;
Working with strings in C# is one thing that any developer does almost on a daily basis. Use ToUpperInvariant rather than ToLowerInvariant when normalizing strings for comparison because it is faster.
6. String is immutable;
Both in the English language and in C# language Immutable is a word that means „cannot change”. What this implies is that once an instance of an immutable type is created it cannot change until the end of the program. When we change the value of an immutable object instead of its value being replaced in memory, a new memory block is allocated.
One such type is string. If we consider the code below, we can see that the value of str is modified 10 times inside the loop and every time it will create a new instance of string and the „pointer” will point to the new instance. The other remaining chunks of memory will be collected at one point by the garbage collector.
var str = "string";
for(var i=0; i<10; i++)
{
str += i;
}
If you have a lot of string concatenations, StringBuilder is a better choice. I’ve seen apps that went down in a matter of seconds with insufficient memory due to string concatenations.
Another thing to consider is that internally the .Net framework supports interning strings. Think what this will return Object.ReferenceEquals(„interning”, „inter”+”ning”).
7. null coalesce operator;
The ??
operator is called the null-coalescing operator. It returns the left-hand operand if the operand is not null; otherwise it returns the right hand operand.
For example
return a == null ? a : b;
will be written more clearly as
return a ?? b;
8. Namespace aliases;
This little gem of functionality let’s you write something like this:
using WinformTimer = System.Windows.Forms.Timer;
using ThreadingTimer = System.Threading.Timer;
Not necessarily useful if you design your class hierarchy in a correct manner, but when you use something that is outside of you control (like the above snippet) it might get in handy.
9. Static constructors;
A static constructor is used to initialize any static data, or to perform a particular action that needs performed once only. It is called automatically before the first instance is created or any static members are referenced.
This means that if you have a list of static fields you can initialize them in the static constructor – useful in particular for reading some configurations into the readonly fields.
Some static contructor properties, as per MSDN:
- A static constructor does not take access modifiers or have parameters.
- A static constructor cannot be called directly.
- The user has no control on when the static constructor is executed in the program.
10. The number of collection types;
Array, ArrayList, BitArray, BitVector32, Dictionary<K,V>, HashTable, HybridDictionary, List<T>, NameValueCollection, OrderedDictionary, Queue, Queue<T>, SortedList, Stack, Stack<T>, StringCollection, StringDictionary.
I was surely surprised to find out there are so many collection types supported in C#. While it might be nice to have so many classes available, it can be kind of difficult to choose the right one for the job. For example using a collection of primitive types with a non-generic collection will result in a performance downgrade due to constant type boxing/unboxing.
Choosing the right collection can be the absolute key to your application performance and maintainability! Having to many choices can be as bad as not having enough choices.
These are only a handful of things that I found out either the hardway – apps not behaving accordingly – or the softway – things spotted by fellow developers during development. Please comment with your little known things you encountered.