How to improve your C# code with Generics

Around 10 months ago we hired a contractor to help us with some projects we needed to start. After 9 months his contracts expired but he couldn’t complete the project he was working on, so as the Lead developer in my team, I had to take his code and complete the project. I’ve spent 20 percent of my time completing the project and 80% optimising it. I’ll write several articles on this experience but for now lets’ concentrate on how to improve a C# code using Generics.

At the end of my optimisation with Generics, the code was 20% shorter and around 30% faster.

What are the Generics?

Generics allow you to use a placeholder instead of a type and you can use them with classes , fields, methods, parameters…

For a class a generic could be:

class Node<T>
    {
        private T value;

        public Node(T currentValue)
        {
            value = currentValue;
        }
    }

We have basically created a node that can contain every type we need. This node can contain an int, a string or a class. It doesn’t matter. It works.

We can also create a Generic method like this:

void DoSomeWork(T myParam) { … }

Also in this case we have a method that can accept every type of parameter (an int, a string, a class…).

How Generics can help us?

Going back to my story, what this contractor needed to do is to get the data from an excel file and evaluate that the value contained in each column is valid. To ba valid, a value must be contained in a list of values.

There are around 20 columns we have to evaluate and to do that he wrote this code (I copied only 3 methods but there were 20 of them):

private static bool ValidOverloadWarning(string value, List<OverloadWarning> listOfOverloadWarnings)
        {
            return listOfOverloadWarnings.Select(x => x.Name.ToLower()).Contains(value.ToLower());
        }

        private static bool ValidOverGeoidUnits(string value, List<GeoidUnits> listOfGeoidUnits)
        {
            return listOfGeoidUnits.Select(x => x.Name.ToLower()).Contains(value.ToLower());
        }

        private static bool ValidOverAltitudeUnits(string value, List<GeoidUnits> listOfAltitudeUnits)
        {
            return listOfAltitudeUnits.Select(x => x.Name.ToLower()).Contains(value.ToLower());
        }

The first think you can notice is that the code looks entirely the same, the only difference is the class contained in every list. Moreover I’ve noticed that all these classes have at least 2 fields: Id, Name.

So I thought: “this code can be optimised and this is the perfect situation to use Generics“.

So I started to create a base class with 2 fields: Id and Name.

public class BaseClass
{
    string Id { get; set; }
    string Name { get; set; }
}

After that I changed all the classes like OverloadWarning, GeoidUnits, GeoidUnits,… so that all could inherit from Base Class. For example the class OverloadWarning became:

public partial class OverloadWarning : GenericItem
{
    //...
    // Other properties
    //...
}

After that I was able to replace 20 almost identical methods with a single one, this:

private static T GetItemByName<T>(string value, List<T> list) where T : GenericItem
{
    return list.FirstOrDefault(x => x.Name.ToLower() == value.ToLower());
}

Just doing this I was able to replace 80 lines (4*20) with just 4. A shorter code means that your code is easier to read and maintain and it’s definitely easier to spot any problems or errors and find improvements.

In facts, I noticed that this contractor was doing 2 operations:

  1. check if the value was in the list
  2. if it was present, then he was retrieving this code from the list

It means that you have to search for that element twice and it takes time, so inside the method I wrote, I just replaced the 2 operations with a single one using the command FirstOrDefault that returns the object if it finds it otherwise it returns null. That’s exactly what I wanted to achieve. This is why my method GetItemByName returns a generic type T instead of a boolean value. Replacing two operations with one, helped me to save another 80 lines of code and it allowed me to halve the time to complete these operations

Conclusion

From this short story we have discovered how the Generics can help us. With the use of Generics I was able to replace 160 lines of code with just 4 lines and to replace 2 quite expensive operations with a single one.

Now my code is shorter, cleaner and more important for the client, it takes around 4 seconds instead of 7 to analyse a file.

What do you think about Generics? Do you have more questions or you need help with them? Write here a comment