C# Best Coding Practices for Developers to Follow
In this article, I will try to list down some C# best practices which a C# developer must follow during development of a project. These common guidelines include use of proper naming conventions in C#, where and why to use value type and reference type variables, using properties instead of public variables in the class, using nullable types, proper exception handling, using runtime constants instead of compile time, using StringBuilder for concatenation, using foreach for collections, proper handling of comments and regions, use of IDisposable Interface etc. Lets dive deep into these C# best practices.
1. Use proper naming convention
A) Always use Camel case (A word with the first letter lowercase, and the first letter of each subsequent word-part capitalized) while declaring variables.
B) Use Pascal (A word with the first letter capitalized, and the first letter of each subsequent word-part capitalized) naming format while declaring Properties.
C) Avoid all uppercase or lowercase names for properties, variables or method names. Use all uppercase when declaring const variables.
D) Never use a name that begins with a numeric character.
E) Always prefer meaningful names for your class, property, method, etc. This will be very useful for you to maintain the code in future. For example, “P” will not give proper meaning for a class. You will find it difficult to know about the class. But if you use “Person”, you will easily understand by it.
F) Never build a different name varied by capitalization. It is a very bad practice. It will not be useful while developing code, as you will not know what is “person” class and what is “Person” class!!! But from the above scenario, it can be very easily understandable that “person” is an instance variable of “Person” class.
G) DO NOT use Abbreviations.
H) DO NOT use Underscores in identifiers.
I) Don't use the same name used in .NET Framework. People who are new to your code have great difficulty to understand it easily.
J) Avoid adding prefixes or suffixes for your identifiers.
K) Always use “I” as prefix for Interfaces. This is a common practice for declaring interfaces.
L) Always add “Exception” as suffix for your custom exception class. It will give better visibility to your exception class.
M) Never prefix or suffix the class name to its property names. It will unnecessarily increase the property name. If “Firstname” is a property of “Person” class, you can easily identify it from that class directly. No need to write “PersonFirstname” or “FirstnameOfPerson”.
N) Prefix “Is”, “Has” or “Can” for boolean properties like “IsVisible”, “HasChildren”, “CanExecute”. These give proper meaning to the properties.
O) Don't add prefix for your controls, instead write proper name to identify the control.
P) Declare all member variables at the top of a class, with static variables at the very top. This will make your code more readable. You dont need to find the variable in class if they are declared at the top and static variables are loaded at first so they have to be on very top so code flow and your code match.
2. Decide between value type and reference type.
Whenever you need to create a type, first ask yourself a question “What you want and Why you want it?”. If you could answer your question, you can decide between the type you want to use. If you want to store your data, use value types and when you want to create an instance of your type by defining the behavior, use reference types. Value types are not Polymorphic whereas, the Reference types can be. Value types are most efficient in terms of memory utilization over reference types and produce less help fragmentation & garbage. If you want to pass values to a method implementation, decide what you want to do and based upon your requirement, decide between value types and reference types. Use of reference type variables actually change the original value but use of value type will create a copy of the original variable and pass across the method. Thus, it protects your original value from accidental changes.
3. Always use Properties instead of Public Variables
Reason behind this is, it makes your code properly encapsulated in OOPs environment. By using getters & setters, you can restrict the user directly accessing the member variables. You can restrict setting the values explicitly thus making your data protected from accidental changes. Also, properties give you easier validation for your data.
4. Use Nullable data types whenever required
Sometimes, you may need to store null as the value of an integer, double or boolean variable. So how can you do this? The normal declaration doesn't allow you to store the null as value. C# now has the feature of nullable data types. Just a small change in your declaration. That’s it!!! You are good to go for storing nullvalues. Only you have to use the “?” modifier. You have to place it just after the type.
5. Prefer Runtime constants over Compile time constants
Runtime constants are always preferred than the Compile time constants. Here you may ask what is runtime constant and what is compile time constant. Runtime constants are those which are evaluated at the runtime and declared with the keyword “readonly”. On the other side, compile time constants are static, evaluated at the time of compilation and declared with the keyword “const”.
6. Prefer String.Format() or StringBuilder for string concatenation
Any operation in the string will create a new object as string is a mutable object. If you want to concatenate multiple strings, it is always better to use string.Format() method or StringBuilder class for the concatenation.
7. Always prefer Foreach() loop
The foreach statement is a variation of do, while or for loops. It actually generates the best iteration code for any collection you have. When you are using collections, always prefer to use the foreach loop as the C# compiler generates the best iteration code for your particular collection. Have a look into the following implementation:
8. Properly utilize Try/Catch/Finally block
Properly utilize the try/catch/finally blocks. If you know that the code you wrote may throw some Exception, use the try/catch block for that piece of code to handle the exception. If you know that, the fifth line of your 10 lines code may throw exception, it is advisable to wrap that line of code only with thetry/catch block. Unnecessary surrounding lines of code with try/catch will slow down your application. Use the finally block to clean up any resources after the call. If you are doing any database call, close the connection in that block. The finally block runs whether your code executes properly or not. So, properly utilize it to cleanup the resources.
Catch only that Exception that you can handle. Catch only those which you expect and order it accordingly. Finally at the end, if you want, add the generic Exception to catch any other unknown exceptions. This gives you a proper way to handle the exception. Suppose, your code is throwingNullReferenceException or ArgumentException. If you directly use the Exception class, it will be very difficult to handle in your application. But by catching the exception properly, you can handle the problem easily.
9. Use IDisposable Interface
Use IDisposable interface to free all the resources from the memory. Once you implement IDisposableinterface in your class, you will get a Dispose() method there. Write code there to free the resources.
10. Keep Class size small
A class should adhere to Single Responsibility Principal.
“The single responsibility principle states that every object should have a single responsibility, and that responsibility should be entirely encapsulated by the class. All its services should be narrowly aligned with that responsibility.”
"THERE SHOULD NEVER BE MORE THAN ONE REASON FOR A CLASS TO CHANGE."
Each responsibility is an axis of change. When the requirements change, that change will be manifest through a change in responsibility amongst the classes. If a class assumes more than one responsibility, then there will be more than one reason for it to change. If a class has more then one responsibility, then the responsibilities become coupled. Changes to one responsibility may impair or inhibit the class’ ability to meet the others. This kind of coupling leads to fragile designs that break in unexpected ways when changed.
11. Avoid obsolete comments
"A comment that has gotten old, irrelevant, and incorrect is obsolete. Comments get old quickly. It is best not to write a comment that will become obsolete. If you find an obsolete comment, it is best to update it or get rid of it as quickly as possible. Obsolete comments tend to migrate away from the code they once described. They become floating islands of irrelevance and misdirection in the code."
Try to avoid comments on individual method or short class. Because most comments i have ever seen is trying to describe the purpose/intentions. Some cases comments are meaningless. Developers writes comments to increase the readability & maintainability . Make sure your comments are not making any noise. It will be great if you could name a method more meaningful instead of comments. I am suggesting because method names are more affective than comments. Most of the comments are meaningless noise.
12. Avoid unnecessary Region in Class
Regions are a feature of VS that allow you to surround blocks of code. It could be a single or multiple methods. The region exists because it is easier to navigate around the large file. The regions are used to hide ugly code or class that have exploded in size . If a class does too many things it also violates the Single Responsibility Principle. So next time whenever you will think for adding a new region to your file take step back and ask that is it possible to separate your region into a separate class.
13. Keep methods short
Split your logic into several small and simple methods. If methods are too long, sometimes it is difficult to handle them. It is always better to use a number of small methods based upon their functionality instead of putting them in a single one. If you break them in separate methods and in future you need to call one part, it will be easier to call rather than replicating the code. Also, it is easier to do unit testing for the small chunks rather than a big code. So, whenever you are writing a piece of code, first think of what you want to do. Based upon that, extract your code in small simple methods and call them from wherever you want. In general, a method should never be more than 10-15 lines long.
14. Avoid too many parameters
Declare a class instead of too many parameters. Creating a class that puts all these parameters together. This is generally a better design and valuable abstraction.
15. Avoid complex expression
Complex expression have some meaning behind them it is just hidden by those multiple expressions. We can Encapsulated the complex expression into that object by using a property. That code will be easier to read.
16. Consider Warnings as Error
We should remove warning as much as possible so they never turn into runtime errors.
17. Avoid multiple Exit points
This rule is very easy to follow. Developer should try to maintain single exit point and entry point
Conclusion: Always write better code. From my point of view better code has following characteristics:
A) Code that is easy to write, modify and extend
B) Code that has values and cares about quality
C) Code that is clean and talks/convey meaning
Why we need conventions
A) They help you transfer knowledge across projects
B) They help you learn code more quickly on a new project
C) They emphasize relationships among related items