How do I write a better code in C#?

Introduction
Before reading this guide, check out the first guide in this series: Introduction to Writing Better C# Code.
A few things you should know prior to reading this guide:
  1. Improvements to C# that were made in its 6th version
2. LINQ in .NET framework
3. Asynchronous programming and Task object in C#
4. Unsafe programming in C#, which allows you to go into memory management
Not Focusing on Performance
It should be noted that I will not talking about changing the program performance, upgrading the efficiency, or decreasing the time taken by the programs to run. You can improve program performance in a matter of seconds by writing good C# code, but the following tips do not guarantee that your code will perform better.
Null Checkup
Do you ever get annoyed by the NullReferenceException, when an object that missed initialization comes back for revenge? There are many benefits to having the null checkups in your programs, not just for better readability but also to ensure that the program does not terminate due to memory issues -such as when a variable does not exist in the memory. These can stack up against the security of your programs and the good UI and UX guidelines that your teams have. Most of the times, a null exception gets raised due to this:
  1. string name = null;
  2.  
  3. Console.WriteLine(name);
In most cases, the compiler itself won't continue until you fix this, but if you somehow manage to trick the compiler into thinking that the variable has a value but on the runtime there is none, a null reference exception will come up. To overcome this, you can do the following:
  1. string name = null;
  2.  
  3. // Try to enter the value, from somewhere
  4. if(name != null) {
  5. Console.WriteLine(name);
  6. }
This safe checkup will ensure that the value is available when this variable is called. Otherwise, it will move in other direction and away from the buggy path. In C# 6 however, there is another way of overcoming this error. Consider a scenario where your database was set up, your table of data was set up, your person was found, but their employment details were not found. Can you get the company where they worked?
  1. var company = DbHelper.PeopleTable.Find(x => x.id == id).FirstOrDefault().EmploymentHistory.CompanyName; // Error
If you did this there will be an error because we were only able to move up a few steps in the list of these values. Then we ended up hitting a null value and everything was lost. C# 6 came up with a new way of overcoming these cases, by using a safe navigation operator after the values and fields which can be null; ?.. Like this:
  1. var company = DbHelper?.PeopleTable?.Find(x => x.id == id)?.FirstOrDefault()?.EmploymentHistory?.CompanyName; // Works
This code will only check for next value if the previous was not null. If the previous value was null, it will return null and save the null as the value for the company, instead of throwing the error. This can come handy where you leave the checkups to the framework itself, but, nonetheless, you still have to check at the end to see if the rest of the values are null or not.
  1. var company = DbHelper.PeopleTable?.Find(x => x.id == id)?.FirstOrDefault()?.EmploymentHistory?.CompanyName;
  2.  
  3. if(company != null) {
  4. // Final process
  5. }
But you get the point, instead of writing the code and checking everything to be null, you can do a simple checkup and perform the actions and logics that you want in your programs. Otherwise, there would be a need for a try...catch wrapper or multiple if...else blocks to control how your program navigates in the system.
Asynchronous Pattern of Programming
If you are programming using C# 5, then you are already using the async/await keywords to bring responsiveness to your applications. If that is not the case, then I would recommend using an asynchronous programming pattern in your application's source code. This can not only increase the responsiveness to your programs but will also increase the readability to your application. A few of the benefits for having asynchronous pattern in your source code are:
Ever since threading, parallelization of the code execution has come into existence. Asynchrony has become a vital part of the programs and applications, so you should also consider using it.
C# String Building
Strings are a vital part of applications nowadays and building the strings can take a lot of time, along with also causing a performance lag in the applications. There are many ways in which you can build the strings in C# programs. Here are a few of those ways:
  1. string str = ""; // Setting it to null would cause additional problems.
  2.  
  3. // Way 1
  4. str = "Name: " + name + ", Age: " + age;
  5.  
  6. // Way 2
  7. str = string.Format("Name: {0}, Age: {1}", name, age);
  8.  
  9. // Way 3
  10. var builder = new StringBuilder();
  11. builder.Append("Name: ");
  12. builder.Append(name);
  13. builder.Append(", Age: ");
  14. builder.Append(age);
  15. str = builder.ToString();
Note that strings in C# are immutable. This means that if you try to update their values, they are recreated and previous handles are removed from the memory. That is why the Way 1 may look like that is the best way to go but, upon further reflection, it is not. The best way to go to is Way 3, which allows you to build up the strings without having to recreate the objects in the memory. However, C# 6 has introduced an entirely new way of building the strings in C# that are much better than the ones that you could imagine previously. The new String interpolation operator $ provides you with the facility of performing the string building in best possible way. The string interpolation goes like this:
  1. static void Main(string[] args)
  2. {
  3. // Just arbitrary variables
  4. string name = "";
  5. int age = 0;
  6.  
  7. // Our interest
  8. string str = $"Name: {name}, Age: {age}";
  9. }
Just a single line of code and the compiler will automatically convert this to the string.Format() version. For a proof, will detail the bytecode that this C# program has generated and that will show you how this would automatically change the syntax to read the formatting of the string.
  1. IL_0000: nop
  2. IL_0001: ldstr ""
  3. IL_0006: stloc.0 // name
  4. IL_0007: ldc.i4.0
  5. IL_0008: stloc.1 // age
  6. IL_0009: ldstr "Name: {0}, Age: {1}"
  7. IL_000E: ldloc.0 // name
  8. IL_000F: ldloc.1 // age
  9. IL_0010: box System.Int32
  10. IL_0015: call System.String.Format
  11. IL_001A: stloc.2 // str
  12. IL_001B: ret
As can be seen, this shows how the syntax is changed back to the one we have already seen. See the IL_0009 for more. This brings a cleaner look to your programs when someone else is reading the programs, along with increasing performance if the strings to be built are smaller. In case of larger strings, use StringBuilder.
Looping Over a Large Collection of Data
What good would an application be, if there is no looping and iteration over a collection of data? In these conditions, there are times when you would have to find a value, find a node, find a record, or perform any other traversing over the collection. In such cases, you really need to make sure you are writing clean code, as this is the area where performance and readability both matter a lot and they are correlated. With a bit of experience, I was able to overcome the wrong way of writing code for reading and traversing the data. That is exactly where LINQ experience should jump in and allow you to write programs that use the best of .NET framework to provide the best coding experience and the best experience to your users and customers.
Previously, you might have done a few of these:
  1. // A function to search for people
  2. Person FindPerson(int id) {
  3. var people = DbContext.GetPeople(); // Returns List<Person>
  4.  
  5. foreach (var person in people) {
  6. if(person.ID == id) {
  7. return person;
  8. }
  9. }
  10.  
  11. // No person found.
  12. return null;
  13. }
  14.  
  15. // Then do this
  16. var person = FindPerson(123);
This can be an easy read for anyone trying to come up in your area. However, the code can be made even simpler and cleaner using LINQ queries in C#. There are two ways in which you can do this. One is a bit SQL-like and other is by using the Where function over the collection and passing the requirement that we have.
  1. // A function to search for people
  2. Person FindPerson(int id) {
  3. var people = DbContext.GetPeople(); // Returns List<Person>
  4.  
  5. return (from person in people
  6. where person.ID == id
  7. select person).ToList().FirstOrDefault();
  8. }
  9.  
  10. // Then do this
  11. var person = FindPerson(123);
This code has a bit of a SQL-like look and would enhance the readability and performance of your code. The function is similar, however, the Where function reads a bit better and leaves everything iteration oriented to .NET framework itself. .NET framework would provide best performance to the applications. Now, let us have a look at another way of writing this query in the same C# code:
  1. // A function to search for people
  2. Person FindPerson(int id) {
  3. var people = DbContext.GetPeople(); // Returns List<Person>
  4.  
  5. return people.FirstOrDefault(x => x.ID == id);
  6. }
  7.  
  8. // Then do this
  9. var person = FindPerson(123);
Notice that the first code returned null in the case when there was no match found. This code also does the same thing. The only the first code was worse was that it had to perform iterations over the collection itself. That local return person; would allow the program to return the control but what would happen if the data was at the last location? The complexity of this algorithm of data search would still be O(n).
Avoid Unsafe Context
C# also supports manual memory management, during the times when you have to pilot your plane yourself. The unsafe context in C# allows you to manipulate the memory, perform pointer arithmetic, read and write the data at memory locations which may not be provided access to, and so on. However, .NET framework does many things to overcome the memory issues, latency, and other stuff that goes around on disk. That also allows .NET framework to overcome the need to actually perform any memory management at all, .NET framework would do that for you.
There are many benefits of using unsafe context, such as when you want to write wrappers around the native C++ libraries. Emgu CV is one such example where you would write some of the code that handles how the native code is managed and to handle the errors in the memory in a way they would be simpler. In this context you can:
There are little to no benefits to this and if you should consider this in your applications, consider it wisely.
Everything Said About Unsafe Was Personal View
I also want to note that everything that I have said about the pros and cons of ‘Unsafe’ are my personal views. I do not frequently use unsafe context in my programs because there is no reason that I should consider using it in my applications. However, if your applications require native memory management, then you can use this context.
Use Lambda Expressions Where Possible
Lambdas come from the arena of functional programming and in C# it has been widely used in many ways, ranging from inline functions all the way down to getter only properties in C# 6. I will show both of the usages in C# that will make the program not only look cooler, but will also have a better performance factor; for that I will show you the IL of that C# code. I personally enjoy using lambdas in many areas, especially when I have to write inline functions in C#. Ever since getter-only properties could be written by using this concept, I have been using them and I personally believe it is better than the previous ways of doing the same thing.
1. Using Lambdas for Inline Functions
You should know some of the well-known delegates of C# programming. There are many fields where they are used, such as in the cases of event handling in your applications. For event handling, you can write your current functions like the following ones:
  1. // Without lamdbas
  2. myBtn.Click += Btn_Click;
  3.  
  4. public void Btn_Click (object sender, EventArgs e) {
  5. // Code to handle the event
  6. }
  7.  
  8. // With the help of lambdas
  9. myBtn.Click += (sender, e) =>
  10. {
  11. // Code to handle the event.
  12. }
Note that compiler will automatically map the objects to their types. This is convenient in many ways as it allows you to write the inline functions in C# that remain with the objects only, unless you want to use them anywhere else too. There is however one disadvantage to this method of handling the events: you cannot remove the event handler once it has been attached. In C# you can -+ but since we don't have any reference to remove the events, we are left only being able to use the separate functions. But, if you don't have to remove the handlers, you should always consider using this way of event handling in your programs.
2. Using Lambdas for Getter-only Properties
In C#, there is a concept of using properties instead of fields. You control how a value is set and how a value is captured from the field. Consider this as an alternative (or similar to) to the getter and setters of Java programming language. Only difference is that you don't have to write them separately somewhere, they are written right in front of the field itself. C# program compilers would then make their own backing fields that are used to store the values.
Basically, you would have to write the getter-only properties like this:
  1. public string Name { get; }
Note that these properties are constant, and you cannot change them once they are set. They were set in the constructors, or (as of C# 6) they are set right in front of it. Like this:
  1. public string Name { get; } = "Afzaal Ahmad Zeeshan";
However, since we already know that this is a constant field and you cannot modify this, then why not create a simple constant property? This is where things get a bit tricky; even a property has to be backed up by a field. In which case, this would do the trick for us:
  1. public string Name => "Afzaal Ahmad Zeeshan";
This is equivalent to writing the following:
  1. 1
  2. public string Name { get { return "Afzaal Ahmad Zeeshan"; } }
But the performance is much better since the getter-only field is converted to a constant field and that field is used in the program anytime this property has to be called.
Final Words
The purpose of this guide series was to allow you to understand and see a few of the ways in which your programs can be easier to read and perform better. The C# compiler itself does the most to increase the quality and efficiency of your code, whereas that is often left to the programmer, and will to make the program work better. There are many other ways of improving the readability, many of these are left to the company's way of writing the programs because most teams require programmers to follow their own programming methods and ways.
#Thanks #viastudy

0 comments:

Post a Comment