Tuesday, June 30, 2009

Best Practices in .NET Coding

Best Practices in .NET Coding
• We all just write code for “working application”• Any developer can do that• There is a need to write “Efficient Code”• Writing 'Efficient Code' is an art and you must learn and practice it.

Working Code vs. Good Code
• A good code is: 1. Reliable2. Maintainable3. Efficient• If any of the above is missing then the code is not a good code and a lot of time could be wasted in trying to understand code and identify issues.

Naming Conventions and Standards
• Pascal Casing - First character of all words are Upper Case and other characters are in lower case.
E.g. BackColor
• Camel Casing - First character of all words, except the first word are Upper Case and other characters are in lower case.
E.g. backColor
Use Pascal casing for Class names
public class HelloWorld
{
... }
Use Pascal casing for Method names
public class HelloWorld
{
void SayHello(string name)
{
...
}
}
Use Camel casing for variables and method parameters
public class HelloWorld
{
int totalCount = 0;
void SayHello(string name)
{
string fullMessage = "Hello " + name;
...
}
}
Do not use Hungarian notation to name variables
• In earlier days most of the programmers liked it - having the data type as a prefix for the variable name and using m_ as prefix for member variables.
Example:
string m_sName;
int nAge;
• However, in .NET coding standards, this is not recommended. Usage of datatype and M_ to represent member variables should not be used. All variables should use camel casing.

Uppercase
• Uppercase characters are often used for enumerations and constant values.• Can also be used for only identifiers that consist of two or fewer letters. For example:
System.IO
System.Web.IO
Use Meaningful, descriptive words to name variables
• Do not use abbreviations. Use name, address, salary etc instead of nam, addr, sal• Do not use single character variable names like i, n, x etc. Use names like index, temp• One exception in this case would be variables used for iterations in loops
for ( int i = 0; i < count; i++ )
{
...
}
• Do not use underscores (_) in variable names. • Do not use variable names that resemble keywords.• Prefix boolean variables, properties and methods with “is” or similar prefixes. Example: private bool isFinished

Namespace Naming Convention
• The general rule for naming namespaces is to use the company name followed by the technology name and optionally the feature and design as follows.
CompanyName.TechnologyName[.Feature][.Design]
For Example:
1.Microsoft.Media
2.Microsoft.Media.Design
Interface Naming Guidelines
• Name interfaces with nouns or noun phrases, or adjectives that describe behavior. For E.g.
public interface IServiceProvider
public interface IFormatable
• Use Pascal Case.• Prefix interface names with the letter “I”, to indicate that the type is an interface.• Do not use the underscore character (_).

Static Field Naming Guidelines
• Use nouns, noun phrases, or abbreviations of nouns to name static fields. • Use Pascal Case. • Do not use a Hungarian notation prefix on static field names. • It is recommended that you use static properties instead of public static fields whenever possible.

Parameter Naming Guidelines
• Use descriptive parameter names.• Use Camel Case for parameter names.• Do not prefix parameter names with Hungarian type notation.• Examples:
Type GetType(string typeName)
string Format(string format, object[] args)
Prefixes for the UI elements
• Use appropriate prefix for the UI elements so that you can identify them from the rest of the variables.• This will help you group all of the UI elements together and easy to access all of them from the intellisense.• Some prefixes are as follows:

Case Sensitivity
• Do not use names that require case sensitivity. Components must be fully usable from both case-sensitive and case-insensitive languages.• Do not create two namespaces with names that differ only by case. For example, a case insensitive language cannot distinguish between the following two namespace declarations.
Namespace IridiumSoftware
Namespace iridiumsoftware
• Do not create a function with parameter names that differ only by case. The following example is incorrect.
void MyFunction(string a, string A)
Case Sensitivity
• Do not create a type with property names that differ only by case. For Example:
int Color {get, set}
int COLOR {get, set}
• Do not create a type with method names that differ only by case. For Example:
void calculate()
void Calculate()
File name should match with class name
• For example, for the class HelloWorld, the file name should be HelloWorld.cs

Indentation and Spacing
• Use TAB for indentation. Do not use SPACES. • Comments should be in the same level as the code.

Example
Good Practice
// Format a message and display
string fullMessage = "Hello " + name;
DateTime currentTime = DateTime.Now;
string message = fullMessage + ", the time is : " + currentTime.ToShortTimeString();
Not so Good Practice
// Format a message and display
string fullMessage = "Hello " + name;
DateTime currentTime = DateTime.Now;
string message = fullMessage + ", the time is : " + currentTime.ToShortTimeString();
#endregion
Indentation and Spacing
• Curly braces ( {} ) should be in the same level as the code outside the braces. Example.

Indentation and Spacing
Use one blank line to separate logical groups of code.
Not a Good Practice:
bool SayHello(string name)
{
string fullMessage = "Hello " + name;
DateTime currentTime = DateTime.Now;
string message = fullMessage + ", the time is : " + currentTime.ToShortTimeString();
MessageBox.Show(message);
if (true)
{
// Do something
// ...
return false;
}
return true;
}
return true;
}
Good Practice:
bool SayHello(string name)
{
string fullMessage = "Hello " + name;
DateTime currentTime = DateTime.Now;
string message = fullMessage + ", the time is : " + currentTime.ToShortTimeString();
MessageBox.Show(message);
if (true)
{
// Do something
// ...
return false;
}
Indentation and Spacing
• There should be one and only one single blank line between each method inside the class. • The curly braces should be on a separate line and not in the same line as if, for etc.
Good:
if ( ... )
{
// Do something
}
Not good:
if ( ... ) {
// Do something
}
Indentation and Spacing
• Use a single space before and after each operator and brackets
Good:
if ( showResult == true )
{
for ( int i = 0; i < 10; i++ )
{
//
}
}
Not Good:
if(showResult==true)
{
for(int i= 0;i<10;i++)
{
//
}
}
Which code looks better?
bool SayHello (string name){
string fullMessage = "Hello " + name;
DateTime currentTime = DateTime.Now;
string message = fullMessage + ", the time is : " + currentTime.ToShortTimeString();
MessageBox.Show ( message );
if ( ... ) {
// Do something //
...
return false;
}
return true;
}
bool SayHello (string name)
{
string fullMessage = "Hello " + name;
DateTime currentTime = DateTime.Now;
string message = fullMessage + ", the time is : " + currentTime.ToShortTimeString();
MessageBox.Show ( message );
if ( ... )
{
// Do something //
...
return false;
}
return true;
}
Follow the best practices for best programming
• Avoid having too large files. If a file has more than 300~400 lines of code, you must consider refactoring code into helper classes. • Avoid writing very long methods. A method should typically have 1~25 lines of code. If a method has more than 25 lines of code, you must consider re factoring into separate methods.

Naming Methods
• Use verbs or verb phrases to name methods. • Use Pascal Case. • Method name should tell what it does. Do not use misleading names. If the method name is obvious, there is no need of documentation explaining what the method does.
Good Practice:
void SavePhoneNumber (string phoneNumber)
{
// Save the phone number.
}
Not a Good Practice:
// This method will save the phone number.
void SaveData (string phoneNumber)
{
// Save the phone number.
}
One job per Method
A method should do only 'one job'. Do not combine more than one job in a single method, even if those jobs are very small
Good Practice:
void main()
{
// Save the address.
SaveAddress(address);
// Send an email to the supervisor to inform that the address is updated.
SendEmail(address, email);
}
void SaveAddress(string address)
{
// Save the address. //
}
void SendEmail(string address, string email)
{
// Send an email to inform the supervisor that the address is changed.
}


Not a good practice:
// Save address and send an email to the supervisor to inform that
// the address is updated.
void main()
{
SaveAddress(address, email);
}
void SaveAddress(string address, string email)
{
// Job 1.
// Save the address.
// ...
// Job 2.
// Send an email to inform the supervisor that the address is changed.
// ...
}
Use of #region
•Use #region to group related pieces of code together.

• Keep private member variables, properties and methods in the top of the file and public members in the bottom.

Types
• Use the C# or VB.NET specific types, rather than the alias types defined in System namespace.
Good: int age;
string name; object contactInfo;
Not Good:
Int16 age;
String name;
Object contactInfo;
Some Key Points
• Do not hardcode numbers. Use constants instead or the better way is to use config files that can be easily changes.• Do not hardcode strings. Use config files. • Convert strings to lowercase or upper case before comparing. This will ensure the string will match even if the string being compared has a different case. For example:
if ( name.ToLower() == “john” )
{
//…
}
Some Key Points
• Use String.Empty instead of “” . For Example:
Good:
If ( name == String.Empty )
{
// do something
}
Not Good:
If ( name == “” )
{
// do something
}
Some Key Points
• Do not make the member variables public or protected. Keep them private and expose public/protected Properties.• Never hardcode a path or drive name in code. Get the application path programmatically and use relative path. • Never assume that your code will run from drive "C:". You may never know, some users may run it from network or from a "Z:". • Do not have more than one class in a single file.

Some Key Points
• Avoid public methods and properties, unless they really need to be accessed from outside the class. Use “internal” if they are accessed only within the same assembly.• Avoid passing too many parameters to a method. If you have more than 4~5 parameters, it is a good candidate to define a class or structure.• If you are opening database connections, sockets, file stream etc, always close them in the finally block. This will ensure that even if an exception occurs after opening the connection, it will be safely closed in the finally block.• Declare variables as close as possible to where it is first used. Use one variable declaration per line.

StringBuilder Class
Use StringBuilder class instead of String when you have to manipulate string objects in a loop.
Using String
public string ComposeMessage (string[] lines)
{
string message = String.Empty;
for (int i = 0; i < lines.Length; i++)
{
message += lines [i];
}
return message;
}
Using StringBuilder
public string ComposeMessage (string[] lines)
{
StringBuilder message = new StringBuilder();
for (int i = 0; i < lines.Length; i++)
{
message.Append( lines[i] );
}
return message.ToString();
}
Watch for unexpected values
Always watch for unexpected values.
If you are using a parameter with 2 possible values, never assume that if one is not matching then the only possibility is the other value.
void example()
{
//Good:
if(memberType == eMemberTypes.Registered)
{
// Registered user…do something…
}
else if ( memberType == eMemberTypes.Guest )
{
// Guest user... do something…
}
else
{
// Un expected user type. Throw an exception
throw new Exception ("Un expected value "+ memberType.ToString() + "’.")
// If we introduce a new user type in future, we can easily find
// the problem here.
}
Not Good:
if(memberType == eMemberTypes.Registered)
{
// Registered user… do something…
}
else
{
// Guest user... do something…
// If we introduce another user type in future, this code will
// fail and will not be noticed.
}
}
Use of ENUM
Use enum wherever required. Do not use numbers or strings to indicate discrete values.
Good:
enum MailType
{
Html,
PlainText,
Attachment
}
void SendMail(string message, MailType mailType)
{
switch (mailType)
{
case MailType.Html:
// Do something
break;
case MailType.PlainText:
// Do something
break;
case MailType.Attachment:
// Do something
break;
default:
// Do something
break;
}
}
Not Good:
void SendMail(string message, string mailType)
{
switch(mailType)
{
case "Html":
// Do something
break;
case "PlainText":
// Do something
break;
case "Attachment":
// Do something
break;
default:
// Do something
break;
}
}
Error Messages
• When displaying error messages, in addition to telling what is wrong, the message should also tell what should the user do to solve the problem. Instead of message like "Failed to update database.", suggest what should the user do: "Failed to update database. Please make sure the login id and password are correct." • Show short and friendly message to the user. But log the actual error with all possible information. This will help a lot in diagnosing problems.

Comments
• Do not write comments for every line of code and every variable declared.
• Write comments wherever required. But good readable code will require very less comments. If all variables and method names are meaningful, that would make the code very readable and will not need much comments.
• Less lines of comments will make the code more elegant. But if the code is not clean/readable and there are less comments, that is worse.
• If you have to use some complex or weird logic for any reason, document it very well with sufficient comments.

Comments
• Use // or /// for comments. Avoid using /* … */• If you initialize a numeric variable to a special number other than 0, -1 etc, document the reason for choosing that value.• The bottom line is, write clean, readable code such a way that it doesn't need any comments to understand. • Do a spell check on comments and also make sure proper grammar and punctuation is used.

Exception Handling
• Never do a 'catch exception and do nothing'. If you hide an exception, you will never know if the exception happened or not. • In case of exceptions, give a friendly message to the user, but log the actual error with all possible details about the error, including the time it occurred, method and class name etc. • Always catch only the specific exception, not generic exception.

Good Exception Handling
void ReadFromFile ( string fileName )
{
try
{
// read from file.
}
catch (FileIOException ex)
{
// log error.
// re-throw exception depending on your case.
throw;
}
}
Bad Exception Handling
void ReadFromFile ( string fileName )
{
try
{
// read from file.
}
catch (Exception ex)
{
// Catching general exception is bad... we will never know whether
// it was a file error or some other error.
// Here you are hiding an exception.
// In this case no one will ever know that an exception happened.
return "";
}
}
Exception Handling
• No need to catch the general exception in all your methods. Leave it open and let the application crash. This will help you find most of the errors during development cycle.
• Do not write try-catch in all your methods. Use it only if there is a possibility that a a specific exception may occur. For example, if you are writing into a file, handle only FileIOException.
• Do not use exceptions for normal or expected errors, or for normal flow of control. (Throwing an exception is an extremely costly operation.)

Exception Handling
• Do not write very large try-catch blocks. If required, write separate try-catch for each task you perform and enclose only the specific piece of code inside the try-catch. This will help you find which piece of code generated the exception and you can give specific error message to the user.• You may write your own custom exception classes, if required in your application. Do not derive your custom exceptions from the base class SystemException. Instead, inherit from ApplicationException.
• All code paths that result in an exception should provide a method to check for success without throwing an exception. For example, to avoid a FileNotFoundException you can call File.Exists. This might not always be possible, but the goal is that under normal execution no exceptions should be thrown.

Exception Handling
• Cleanup code should be put in finally blocks. Never do cleanup code, e.g., closing streams, restoring state (as the mouse cursor), outside of a finally block.
Do not create methods that throw checked Exceptions like NullReferenceException or IndexOutOfRangeException.
Thank You!

No comments:

Post a Comment