Clean Code In C#

In this article, we will learn about how to write clean C# code. As we know that writing code is not much difficult, but writing clean and reusable code is not easy in work pressure time. but If you write better code your senior will impress more, which can help you in your career growth.
 
This will come from practice do more and more. so here we will cover the following things,
  • Use a Good IDE
  • Use Meaningful Names
  • Use Case Notation
  • Reuse Code
  • Design Patterns
  • Remove Unused Code
  • Method Chaining
  • Async/Await
  • Ternary Operator
  • Null Coalescing Operator
  • Prefer String Interpolation
  • Avoid Large Parameters in method
  • Log Exception

Use a Good IDE

 
First of all before start to write the code choose best IDE for your code development. In case of C# code writing we have Visual Studio, Visual Studio Code it's free provided by Microsoft. As this IDE provided code fomatting and intellisens to correct our code.
 

Use Meaningful Names

 
You know if we work on bigger project then according to business need we required a lot of local variable and class proporty names. That at time we confuse to decide better name. but we should tale time and analyze the best name for any varible, class name or method name, parameter as well.
 
so it should be selft explanatory.
 
Here i am giving some example for this.
  1. // Bad  
  2. int i1;  
  3. string s1;  
  4. date d1;  
  5.   
  6. // Good  
  7. int age;  
  8. string name;  
  9. date createdDate;  
so that it will easy for other developer to understand the concept.
 

Use Case Notation

 
We should also follow that how Microsoft writing his code, so this is very easy to see just click on an inbuilt method to check for it's definiton, you will see a lot of method and proporty how microsoft define.
 
here is the example,
  1. //    
  2. // Summary:    
  3. // Information about the current routing path.    
  4. public class RouteData {  
  5.     //    
  6.     // Summary:    
  7.     // Creates a new instance of Microsoft.AspNetCore.Routing.RouteData instance.    
  8.     public RouteData();  
  9.     //    
  10.     // Summary:    
  11.     // Creates a new instance of Microsoft.AspNetCore.Routing.RouteData instance with    
  12.     // values copied from other.    
  13.     //    
  14.     // Parameters:    
  15.     // other:    
  16.     // The other Microsoft.AspNetCore.Routing.RouteData instance to copy.    
  17.     public RouteData(RouteData other);  
  18.     //    
  19.     // Summary:    
  20.     // Creates a new instance of Microsoft.AspNetCore.Routing.RouteData instance with    
  21.     // the specified values.    
  22.     //    
  23.     // Parameters:    
  24.     // values:    
  25.     // The Microsoft.AspNetCore.Routing.RouteValueDictionary values.    
  26.     public RouteData(RouteValueDictionary values);  
  27.     //    
  28.     // Summary:    
  29.     // Gets the data tokens produced by routes on the current routing path.    
  30.     public RouteValueDictionary DataTokens {  
  31.         get;  
  32.     }  
  33.     //    
  34.     // Summary:    
  35.     // Gets the list of Microsoft.AspNetCore.Routing.IRouter instances on the current    
  36.     // routing path.    
  37.     public IList < IRouter > Routers {  
  38.         get;  
  39.     }  
  40.     //    
  41.     // Summary:    
  42.     // Gets the values produced by routes on the current routing path.    
  43.     public RouteValueDictionary Values {  
  44.         get;  
  45.     }  
  46.     //    
  47.     // Summary:    
  48.     // Creates a snapshot of the current state of the Microsoft.AspNetCore.Routing.RouteData    
  49.     // before appending router to Microsoft.AspNetCore.Routing.RouteData.Routers, merging    
  50.     // values into Microsoft.AspNetCore.Routing.RouteData.Values, and merging dataTokens    
  51.     // into Microsoft.AspNetCore.Routing.RouteData.DataTokens.    
  52.     // Call Microsoft.AspNetCore.Routing.RouteData.RouteDataSnapshot.Restore to restore    
  53.     // the state of this Microsoft.AspNetCore.Routing.RouteData to the state at the    
  54.     // time of calling Microsoft.AspNetCore.Routing.RouteData.PushState(Microsoft.AspNetCore.Routing.IRouter,Microsoft.AspNetCore.Routing.RouteValueDictionary,Microsoft.AspNetCore.Routing.RouteValueDictionary).    
  55.     //    
  56.     // Parameters:    
  57.     // router:    
  58.     // An Microsoft.AspNetCore.Routing.IRouter to append to Microsoft.AspNetCore.Routing.RouteData.Routers.    
  59.     // If null, then Microsoft.AspNetCore.Routing.RouteData.Routers will not be changed.    
  60.     //    
  61.     // values:    
  62.     // A Microsoft.AspNetCore.Routing.RouteValueDictionary to merge into Microsoft.AspNetCore.Routing.RouteData.Values.    
  63.     // If null, then Microsoft.AspNetCore.Routing.RouteData.Values will not be changed.    
  64.     //    
  65.     // dataTokens:    
  66.     // A Microsoft.AspNetCore.Routing.RouteValueDictionary to merge into Microsoft.AspNetCore.Routing.RouteData.DataTokens.    
  67.     // If null, then Microsoft.AspNetCore.Routing.RouteData.DataTokens will not be changed.    
  68.     //    
  69.     // Returns:    
  70.     // A Microsoft.AspNetCore.Routing.RouteData.RouteDataSnapshot that captures the    
  71.     // current state.    
  72.     public RouteDataSnapshot PushState(IRouter router, RouteValueDictionary values, RouteValueDictionary dataTokens);  
  73.     //    
  74.     // Summary:    
  75.     // A snapshot of the state of a Microsoft.AspNetCore.Routing.RouteData instance.    
  76.     public readonly struct RouteDataSnapshot {  
  77.         //    
  78.         // Summary:    
  79.         // Creates a new instance of Microsoft.AspNetCore.Routing.RouteData.RouteDataSnapshot    
  80.         // for routeData.    
  81.         //    
  82.         // Parameters:    
  83.         // routeData:    
  84.         // The Microsoft.AspNetCore.Routing.RouteData.    
  85.         //    
  86.         // dataTokens:    
  87.         // The data tokens.    
  88.         //    
  89.         // routers:    
  90.         // The routers.    
  91.         //    
  92.         // values:    
  93.         // The route values.    
  94.         public RouteDataSnapshot(RouteData routeData, RouteValueDictionary dataTokens, IList < IRouter > routers, RouteValueDictionary values);  
  95.         //    
  96.         // Summary:    
  97.         // Restores the Microsoft.AspNetCore.Routing.RouteData to the captured state.    
  98.         public void Restore();  
  99.     }  
so I hope now we should clear to write the case.
 
In summany use can say that for camel case use the first letter of the first word of the variable will be in lower case, then after that use first letter of every other word that in upper case. This is for while naming your local variables and method arguments.
  1. // Bad  
  2. int MyAGE  
  3. string MYNaMe  
  4.   
  5. // Good  
  6. int employeeAge;  
  7. string FirstName;  
and for pascal case : the first letters of each words should be in Upper Case. We should use this kind of naming for Methods and Classes.
  1. // Bad  
  2. addInteger(int a, int b);  
  3. addEmployee(object emp);  
  4.   
  5. // Good  
  6. AddInteger(int a, int b);  
  7. AddEmployee(EmployeeDto employee);  

Reuse Code

 
As we know in coding a lot of code we can reuse if we thing before the implementation. so keep in mind whatever we are going to write the code we can resue in other places or not.
 
If we write the reusable code it will reduce a lot of line of code and makes it highly efficient. Other thing we should avoid to make a large class, always try to make it small size. As we know that Solid Principles, suggest you must segregate classes to small class so that we can use it as a single responsibilty function. So that it will helps us to acheive loosely coupled code.
 

Design Patterns

 
This pattern practice will come after experience and experience to write and think more code. Design patterns are basically patterns that can provide a resuable solution.
we have a lot of design pattern that need to learn.
 
Creational Design Pattern
  1. Factory Method
  2. Abstract Factory
  3. Builder
  4. Prototype
  5. Singleton
Structural Design Patterns
  1. Adapter
  2. Bridge
  3. Composite
  4. Decorator
  5. Façade
  6. Flyweight
  7. Proxy
Behavioral Design Patterns
  1. Chain of Responsibility
  2. Command
  3. Interpreter
  4. Iterator
  5. Mediator
  6. Memento
  7. Observer
  8. State
  9. Strategy
  10. Visitor
  11. Template Method
Note
This design pattern you can learn from any resources,Here I can suggest to go through with this;
 
 

Remove Unused Code

 
In general pracrice we are wrting code, then if we feel it is not in use then we are not removing the code and start wrting another code for the same. that is ok that you can take some refernce from your code. but I would suggest after finishing your logic and testing remove unused code, before checking, for reviewing with your senior.
 

Method Chaining

 
Method chaining is the concept that multiple method calling together.
 
This is one of the good example.
  1. services.AddHealthChecks().AddSqlServer(_configuration.GetConnectionString("DbConn"));  
Here in this code we are calling three methods( AddHealthChecks, AddSqlServer, GetConnectionString) together.
 
The advantage of this, it will compile in a single line,
 
Let's take another example here:
  1. public string ReplaceStrings()  
  2. {  
  3.    string name = "Hamid Ali";  
  4.    return name.Replace("H""A").Replace("A""B").Replace(" ""_");  
  5. }  
I hope now it will be clear!!!
 

Async/Await

 
In latest version of C# Microsoft provided Asynchronous programming, this is a very good concept that we should go with this approach!
 
Asynchronous Programming helps us to improve the overall efficiency of application when dealing with large computation functions that can take some time to finish computing.
 
Because of such function executions, may take more time to complete application so it seem to be frozen to the end-user. This unnecessary time taken at UI side in bad for user experience. In this cases, we should use async methods to free the main thread.
 
Read async programming from here,

Avoid ‘throw ex;’ In The Catch Block

 
As we know for catching exception stack trace is important part. so if we use throw ex, we could loose to store stack trace. So better to use throw only in catch block
  1. // Bad    
  2. try {  
  3.     // ToDo code here    
  4. catch (Exception ex) {  
  5.     throw ex;  
  6. }  
  7. // Good    
  8. try {  
  9.     // Do something..    
  10. catch (Exception ex) {  
  11.     throw;  
  12. }  

Ternary Operator

 
Ternary Operator is the concept that we can apply using ? and : symbol for checking conditio, as we can say that the alternative of if else condition. The other benifit is that it reduce the line of code.
 
Look this example,
  1. // Approach 1    
  2. public string Validate(int age) {  
  3.     if (age == 10) {  
  4.         return "Age is 10";  
  5.     } else {  
  6.         return "Age is not 10";  
  7.     }  
  8. }  
  9. // Approach 2    
  10. public string Validate(int age) {  
  11.     return age == 10 ? "Age is 10" : "Age is not 10";  
  12. }   
see this Ternary Operator reduces line of code and look much cleaner.
 

Null Coalescing Operator

 
For checking Null of any entity Microsoft provided ?? operator it is known as Null Coalescing. using this operator we can identify whether this given entity is null or not.
 
Example
  1. public Employee Validate(Employee employee) {  
  2.     if (employee != null) {  
  3.         return employee;  
  4.     } else {  
  5.         return new Employee() {  
  6.             Name = "Smith"  
  7.         };  
  8.     }  
  9.  
Here in this example I took a class enployee and checking if employee is not or not.
 
Same thing we can do using Null Coalescing Operator as below it reduces line of code and look much cleaner.:
  1. public Employee Validate(Employee employee)  
  2. {  
  3.    return employee ?? new Employee() { Name = "John" };  
  4. }  
String Interpolation
 
For adding any number of string in general we are using + operator, but in C# 6, the String Interpolation feature was introduced.
 
Using + operator,
  1. public string Employee Validate(Employee employee)  
  2. {  
  3.    return "Employee Name is " + employee.Name + ". Age is " + employee.Age;  
  4. }  
so we should take this advantage
 
usong $ sysmbol
  1. public string Employee Validate(Employee employee)  
  2. {  
  3.    return $"Employee Name is {employee.Name}. Age is {employee.Age}";  
  4. }  
for small kind of method we should use that Expression Bodied Methods
  1. public string Message() => "Hello World!";  

Avoid Large Parameters in method

 
some times we required to pass a lot of parameter, in this case we should avoid to pass large parameter. Insted of this we can create an object and pass in method parameter.
 
Here in thid example we can see that,
  1. // Bad    
  2. public Employee Validate(int id, string name, string company, string city, int age, DateTime dateOfBirth) {  
  3.     return new Employee() {  
  4.         Age = age,  
  5.             Name = name,  
  6.             Company = company,  
  7.             // etc.    
  8.     };  
  9. }  
  10. // Good    
  11. public Employee Validate(Employee employee) {  
  12.     return employee;  
  13. }  
Log Exception
 
This is a good approach to add try catch block and log the error.
  1. // Bad    
  2. public void Validate(Employee employee) {  
  3.     try {  
  4.         TODO();  
  5.     } catch {}  
  6. }  
  7. // Good    
  8. public void Validate(Employee employee) {  
  9.     try {  
  10.         TODO();  
  11.     } catch (Exception ex) {  
  12.         LogException(ex);  
  13.     }  
  14. }   

Summary

 
Congratulations on following the steps and reaching out here. I hope you liked this tutorial and please share it with others.
 
Do you have any suggestions or additions? Please mention below in comment section. Thanks for taking your valuable time to read the full article.


Similar Articles