Writing Effective C# Code

I am here to talk about better coding practices which I learned over time. Typically, we realize something better once we understand that the way we coded it is now putting some constraint or accommodating new changes are getting messier.

In this article, we will go through some of the common coding misses and ways to address them. These proven coding practices can help with writing better, effective, and maintainable code.

Let’s start by putting some fundamental questions.

What is an effective code?

As per my point of view, an effective code should have the following characteristics.

  • It should be clean/crisp and yet complete.
  • It should be scalable and reusable with minimum efforts.
  • Until coding/naming convention is not explicitly customized for any project, it should follow default coding convention suggested by community tools like StyleCop/ FxCop / Resharper etc.

Why is effective code important?

Writing effective code is extremely important in any enterprise software development. Below are some of the points emphasizing the importance.

  • It helps to understand better when re-visiting the code.
  • It helps reviewer to understand the code easily.
  • It helps when someone else fixes the bugs.
  • It helps when someone tries to reuse the code.

How to write effective or quality code?

Let’s discuss some of the guidelines as follows.

  1. Keep crisp and yet complete logic

    Please look at all three approaches below to understand it.

    Example-1 (Bad code)

    1. if (i % 3 == 0 && i % 5 == 0)  
    2.                 {  
    3.                     Console.WriteLine("HiPrakash");  
    4.                 }  
    5.                 else if (i % 3 == 0)  
    6.                 {  
    7.                     Console.WriteLine("Hi");  
    8.                 }  
    9.                 else if (i % 5 == 0)  
    10.                 {  
    11.                     Console.WriteLine("Prakash");  
    12.                 }  
    13.                 else  
    14.                 {  
    15.                     Console.WriteLine(i);  
    16.                 }  

    Example-2 (Good code)

    1. string output = "";  
    2.                 if (i % 3 == 0) { output = "Hi"; }  
    3.                 if (i % 5 == 0) { output = output + "Prakash"; }  
    4.                 if (output == "") { output = i.ToString(); }  
    5.                 Console.WriteLine(output);  

    Example-3 (Better code)

    1. string output = string.Empty;  
    2.                 if (number % 3 == 0)  
    3.                 {  
    4.                     output = "Hi";  
    5.                 }  
    6.                 if (number % 5 == 0)  
    7.                 {  
    8.                     output += "Prakash";  
    9.                 }  
    10.                 if (string.IsNullOrEmpty(output))  
    11.                 {  
    12.                     output = number.ToString();  
    13.                 }  
    14.                 return output;  
    As you can see that final approach (Example-3) is crisp, clean, and complete.
  1. Use meaningful method names

    Keeping meaningful names is very important so that the new users can get sense of the functionality just by looking at the method names.

    Example-1 (Bad code)

    1. public int ReturnOutput (string name)  
    2.         {  
    3.             int id = -1;  
    4.             //empid = Call some API to get emp Id for any name  
    5.             return id;  
    6.         }   

    Example-2 (Good code)

     

    1. public int GetEmpId(string name)  
    2.         {  
    3.             int id = -1;  
    4.             //id = Call some API to get emp Id for any name  
    5.             return id;  
    6.         }  
    Example-3 (Better code)
    1. public int GetEmpIdByName(string empName)  
    2.         {  
    3.             int empid = -1;  
    4.             //empid = Call some API to get emp Id for any name  
    5.             return empid;  
    6.         }  
    As you can see, the Example-1 doesn’t give any sense of what this method is supposed to do however the later examples are taking care of this. The Example-3 even takes care about logical variable namings.
  1. Refactor the code whenever and wherever possible

    Refactoring of code is very important in keeping the code clean and to increase the modularity and reusability of code.

    Example-1 (Bad code)

    1. public void PrintPrimeNumbersTillHundred()  
    2.         {  
    3.             for (int number = 1; number <= 100; number++)  
    4.             {  
    5.                 int i;  
    6.                 for (i = 2; i < number; i++)  
    7.                 {  
    8.                     if (number % i == 0)  
    9.                     {  
    10.                         break;  
    11.                     }  
    12.                 }  
    13.                 if (i == number)  
    14.                 {  
    15.                     Console.WriteLine("Number {0} is Prime", i);  
    16.                 }  
    17.             }  
    18.         }   

    Example-2 (Good code)

     

    1. public void PrintPrimeNumbersTillHundred2()  
    2.         {  
    3.             for (int i = 1; i <= 100; i++)  
    4.             {  
    5.                 if (IsNumberPrime(i))  
    6.                 {  
    7.                     Console.WriteLine("Number {0} is Prime", i);  
    8.                 }  
    9.             }  
    10.         }  
    11.   
    12.         private static bool IsNumberPrime(int number)  
    13.         {  
    14.             int i;  
    15.             for (i = 2; i < number; i++)  
    16.             {  
    17.                 if (number % i == 0)  
    18.                 {  
    19.                     return false;  
    20.                 }  
    21.             }  
    22.             if (i == number)  
    23.             {  
    24.                 return true;  
    25.             }  
    26.             return false;  
    27.         }  
    As you can see in Example-1, the logic of prime number is not factored, hence if there is a need to reuse it, that's not possible. However, Example-2 takes care of it by factoring it to new method.
  1. Use Auto-Implemented Properties

    Unless there is some custom logic needed in getter/setter or you are developing for less than .NET 3.0 framework (although it’s unlikely), go for auto auto-Implemented properties instead of creating them in traditional way using private variable.

    Example-1 (Bad code)

    1. private string empId;  
    2.         public string EmpId  
    3.         {  
    4.             get  
    5.             {  
    6.                 return empId;  
    7.             }  
    8.             set  
    9.             {  
    10.                 empId = value;  
    11.             }  
    12.         }   

    Example-2 (Good code)

    1. public string EmpId { get; set; }  

    As you can see that auto implemented properties (in Example-2) provide clean way of declaration.

  1. Use object initializer

    Starting from .NET 3.0 onwards, go for object initializer instead of traditional way of first creating object and then assigning values.

    Example-1 (Bad code)

    1. Employee emp = new Employee();  
    2.             emp.EmpId = 101;  
    3.             emp.EmpName = "Prakash";  
    4.             emp.EmpCity = "Satna";   

    Example-2 (Good code)

    1. Employee emp = new Employee() { EmpId = 101, EmpName = "Prakash", EmpCity = "Satna" };  

    Clearly, object initializer provides crisp syntax to keep code clean.

  1. Follow logical ordering of members

    It’s easy to navigate members when they are placed in logical order. Declare all member variables at the top of a class followed by methods. Also, keep private members first, followed by Public. Just to add - if you are using tools like StyleCop etc, these things are enforced by default.

    Example-1 (Bad code)

    1. public class Employee  
    2.     {          
    3.         public int EmpId { get; set; }  
    4.         public void GetSalaryDetails()  
    5.         {  
    6.             //Call some API to get data  
    7.         }  
    8.         private DateTime EmpDateOfBirth;  
    9.         public string EmpName { get; set; }  
    10.     }   

    Example-2 (Good code)

    1. public class Employee  
    2.     {  
    3.         private DateTime EmpDateOfBirth;  
    4.         public int EmpId { get; set; }  
    5.         public string EmpName { get; set; }  
    6.         public void GetSalaryDetails()  
    7.         {  
    8.             //Call some API to get data  
    9.         }         
    10.     }  

    I hope you would agree that in Example-2, it’s easier to navigate to members.

  1. Avoid using very short abbreviations

    Using very short names sometimes doesn’t give the sense of their parent object and hence is confusing. Below are the examples of short and recommended names.

    Example

    1. ProductSales prSal;   //Avoid  
    2. ProductSales productSales;  //Recommanded  
  2. Use casing properly

    Unless explicitly specified, use pascal casing for classes and method names and camel casing for method arguments and local variables. Below is the example of pascal casing (ProductSales, GetSalesProfitByMonth) and camel casing (monthCode).

    Example

    1. public class ProductSales  
    2.     {  
    3.         public void GetSalesProfitByMonth(int monthCode)  
    4.         {  
    5.             //...  
    6.         }  
    7.     }  

  1. Use singular name

    Try to use singular name for Class/Interface/Enum. Also prefix interface with I.

    Example

    1. public enum Color  
    2.     {  
    3.         Red,  
    4.         Green,  
    5.         Blue  
    6.     }  
    7.   
    8.     public interface IProductSales  
    9.     {  
    10.   
    11.     }  

    And last but not least!!


  1. Review! Review! Review!!

    Code review is very important in any successful software development. Code review helps in early detection of any code or design anomaly and can potentially minimize the number of bugs reported in the QA phase.

Conclusion

In this article, we have gone through some of the common coding mistakes and ways to address them. We have also gone through code snippets for them in order to understand the better and effective coding style.

Hope you liked the arrticle. I look forward to your comments/suggestions.