Good Programming Practices A guide to your “code”
Prepared by Dinesh Bhat
This document contrains 41 pages
TABLE OF CONTENTS 1 INTRODUCTION....................................................................................................................................................... ........3 2 GUIDELINES ............................................................................................................................................ .......................3 2.1 BEST PRACTICES FOR GOOD PROGRAMMING..................................................................................................3 2.1.1 AVOID HAVING TOO LARGE FILES.....................................................................................................................3 2.1.2 AVOID WRITING VERY LONG METHODS............................................................................................................3 2.1.3 WHITE SPACE.............................................................................................................................................. ..........3 2.1.4 BLANK SPACES............................................................................................................................ ........................3 2.1.5 PARENTHESES............................................................................................................................................ ..........4 2.1.6 NAMING GUIDELINES.............................................................................................................................. .............4 2.1.6.1 CAPITALIZATION STYLES ............................................................................................................................4 2.1.6.2 CASE SENSITIVITY...................................................................................................................................... ...6 2.1.6.3 ABBREVIATIONS.......................................................................................................................... ..................7 2.1.6.4 NAMESPACE NAMING GUIDELINES............................................................................................................7 2.1.6.5 CLASS NAMING GUIDELINES.......................................................................................................................7 2.1.6.6 INTERFACE NAMING GUIDELINES...............................................................................................................8 2.1.6.7 ATTRIBUTE NAMING GUIDELINES...............................................................................................................9 2.1.6.8 ENUMERATION TYPE NAMING GUIDELINES............................................................................................10 2.1.6.9 STATIC FIELD NAMING GUIDELINES.........................................................................................................10 2.1.6.10 PARAMETER NAMING GUIDELINES.........................................................................................................11 2.1.6.11 METHOD NAMING GUIDELINES................................................................................................................11 2.1.6.12 PROPERTY NAMING GUIDELINES...........................................................................................................11 2.1.6.13 EVENT NAMING GUIDELINES...................................................................................................................12 2.1.6.14 DELEGATE NAMING GUIDELINES............................................................................................................13 2.1.7 USE EARLY BINDING FOR BETTER PERFORMANCE.....................................................................................14 2.1.8 RETURNING VALUES................................................................................................................................ ..........14 2.1.9 STATEMENTS................................................................................................................................................ .......15 2.1.9.1 SIMPLE STATEMENTS.................................................................................................................................15 2.1.9.2 COMPOUND STATEMENTS.........................................................................................................................15 2.1.9.3 IF, IF-ELSE, IF ELSE-IF ELSE STATEMENTS..............................................................................................15 2.1.9.4 FOR STATEMENTS...................................................................................................................... .................16 2.1.9.5 WHILE STATEMENTS...................................................................................................................................16 2.1.9.6 DO-WHILE STATEMENTS........................................................................................................................... ..17 2.1.9.7 SWITCH STATEMENTS............................................................................................................................... ..17 2.1.9.8 USING STATEMENT............................................................................................................................... .......17 2.1.10 METHOD NAME SHOULD TELL WHAT IT DOES............................................................................................18 2.1.11 A METHOD SHOULD DO ONLY 'ONE JOB'......................................................................................................18 2.1.12 PAY ATTENTION TO INITIALIZATION ORDER.................................................................................................19 2.1.13 DO NOT HARDCODE NUMBERS......................................................................................................................19
2.1.14 STRING CONCATENATION................................................................................................................... ............20 2.1.15 AVOID USING MANY MEMBER VARIABLES...................................................................................................20 2.1.16 USE ENUM WHEREVER REQUIRED................................................................................................................20 2.1.17 DO NOT MAKE THE MEMBER VARIABLES PUBLIC OR PROTECTED........................................................21 2.1.18 NEVER HARDCODE A PATH OR DRIVE NAME IN CODE...............................................................................21 2.1.19 "SELF CHECK" WHEN THE APPLICATION STARTSUP.................................................................................21 2.1.20 ERROR MESSAGES SHOULD HELP THE USER TO SOLVE THE PROBLEM..............................................22 2.1.21 AVOID COMMENTING EVERY LINE OF CODE................................................................................................22 2.1.22 EXCEPTION HANDLING............................................................................................................................ ........22 2.1.23 MEMORY MANAGEMENT.................................................................................................................................23 2.1.24 DATA ACCESS............................................................................................................................ .......................24 2.1.25 SERIALIZATION............................................................................................................................. ....................25 2.1.26 MULTITHREADING................................................................................................................... .........................26 2.1.27 REMOTING....................................................................................................................................... ..................28 2.1.28 ENTERPRISE SERVICES .................................................................................................................................30 2.1.29 SECURITY................................................................................................................................... .......................31 2.2 ASP.NET........................................................................................................................................................... .......33 2.2.1 DISABLE SESSION STATE WHEN NOT IN USE................................................................................................33 2.2.2 CHOOSE YOUR SESSION STATE PROVIDER CAREFULLY............................................................................34 2.2.3 USAGE OF VIEWSTATE......................................................................................................................................34 2.2.4 AVOID EXCESSIVE ROUND TRIPS TO THE SERVER.......................................................................................34 2.2.5 USE PAGE.ISPOSTBACK TO AVOID EXTRA WORK ON A ROUND TRIP.......................................................34 2.2.6 REUSE WORK BY CACHING..............................................................................................................................35 2.2.7 REMOVE CODE FROM CONTENT AS MUCH AS POSSIBLE...........................................................................35 2.2.8 NEW PAGE DIRECTIVES................................................................................................................................ .....35 2.2.9 USE RESPONSE.WRITE FOR STRING CONCATENATION..............................................................................35 2.2.10 DO NOT FORGET TO DISABLE DEBUG MODE..............................................................................................36 2.2.11 USE SERVER CONTROLS ONLY WHEN APPROPRIATE...............................................................................36 2.2.12 USE THE WEB.CONFIG/MACHINE.CONFIG FILE TO STORE APPLICATION WIDE DATA..........................36 2.2.13 EXCEPTION HANDLING IN ASP.NET...............................................................................................................37 2.3 GENERAL TIPS................................................................................................................................... ...................38
Good Programming Practices
1
INTRODUCTION This document provides coding techniques and programming practices for improving the quality of source code. This can be used as a part of the induction manual when new team members/technical members are introduced to the team. This document highlights some of the best practices for coding in c# language and sets some general guidelines for effective coding.
2
GUIDELINES
2.1
BEST PRACTICES FOR GOOD PROGRAMMING
2.1.1
AVOID HAVING TOO LARGE FILES. If a file has more than 300~400 lines of code, you must consider refactoring code into helper classes.
2.1.2
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.
2.1.3
WHITE SPACE Blank lines improve readability by setting off sections of code that are logically related. Two blank lines should always be used between sections of a source file. That is, two blank lines should follow: •
the opening comment
•
the package statement
•
the import statements
•
the class and interface declaration
One blank line should always be used in the following circumstances:
2.1.4
•
Between methods
•
Between the local variables in a method and its first statement
•
Before a block or single-line comment.
•
Between logical sections inside a method to improve readability.
•
Use common sense and don't be afraid to put blank lines!
BLANK SPACES Blank spaces should be used in the following circumstances: •
A blank space should appear after commas in argument lists.
•
All binary operators except. Should be separated from their operands by spaces. Blank spaces should never separate unary operators such as unary minus, increment ("++"), and decrement ("--") from their operands.
Page 3 of 42
Good Programming Practices •
The expressions in a “for” statement should be separated by blank spaces.
example: public void aMethod(int a, int b, int c, int d) { a += c + d; a = (a + b) / (c * d); printSize("size is " + foo); for(expr1; expr2; expr3) { /* No Body */ }
} 2.1.5
PARENTHESES It is generally a good idea to use parentheses liberally in expressions involving mixed operators to avoid operator precedence problems if(a == b && c == d) if((a == b) && (c == d))
// AVOID! // RIGHT
2.1.6
NAMING GUIDELINES A consistent naming pattern is one of the most important elements of predictability and discoverability in a managed class library.
2.1.6.1
Capitalization Styles Describes the Pascal case, camel case, and uppercase capitalization styles to use to name identifiers in class libraries. •
Pascal case
The first letter in the identifier and the first letter of each subsequent concatenated word are capitalized. You can use Pascal case for identifiers of three or more characters. Example: BackColor
•
Camel case
The first letter of an identifier is lowercase and the first letter of each subsequent concatenated word is capitalized. Example: backColor
Page 4 of 42
Good Programming Practices
•
Uppercase All letters in the identifier are capitalized. Use this convention only for identifiers that consist of two or fewer letters.
Example: Sysetm.IO System.Web.UI
The following table summarizes the capitalization rules and provides examples for the different types of identifiers: Identifier
Case
Example
Class
Pascal
AppDomain
Enum type
Pascal
ErrorLevel
Enum values
Pascal
FatalError
Event
Pascal
ValueChange
Exception class
Pascal
WebException Note Always ends with the suffix Exception.
Read-only Static field
Pascal
RedValue
Interface
Pascal
IDisposable Note Always begins with the prefix I.
Method
Pascal
ToString
Namespace
Pascal
System.Drawing
Parameter
Camel
typeName
Property
Pascal
BackColor
Protected instance field
Camel
Public instance field
Pascal
redValue Note Rarely used. A property is preferable to using a protected instance field. RedValue Note Rarely used. A property is preferable to using a public instance field.
Page 5 of 42
Good Programming Practices
2.1.6.2
Case Sensitivity •
Do not use names that require case sensitivity. Components must be fully usable from both casesensitive and case-insensitive languages.
•
Case-insensitive languages cannot distinguish between two names within the same context that differ only by case. Therefore, you must avoid this situation in the components or classes that you create.
•
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 ee.cummings; namespace Ee.Cummings;
•
Do not create a function with parameter names that differ only by case. The following example is incorrect. void MyFunction(string a, string A)
•
Do not create a namespace with type names that differ only by case. In the following example, Point p and POINT p are inappropriate type names because they differ only by case. System.Windows.Forms.Point p System.Windows.Forms.POINT p
•
Do not create a type with property names that differ only by case. In the following example, int Color and int COLOR are inappropriate property names because they differ only by case. int Color { get, set } int COLOR { get, set }
•
Do not create a type with method names that differ only by case. In the following example, calculate and Calculate are inappropriate method names because they differ only by case. void calculate() void Calculate()
Page 6 of 42
Good Programming Practices
2.1.6.3
Abbreviations •
To avoid confusion and guarantee cross-language interoperation, follow these rules regarding the use of abbreviations:
• Do not use abbreviations or contractions as parts of identifier names. Example: Good: GetWindow Not Good: GetWin
2.1.6.4
•
Do not use acronyms that are not generally accepted in the computing field.
•
Where appropriate, use well-known acronyms to replace lengthy phrase names. For example, use UI for User Interface and OLAP for On-line Analytical Processing.
•
When using acronyms, use Pascal case or camel case for acronyms more than two characters long. For example, use HtmlButton or htmlButton. However, you should capitalize acronyms that consist of only two characters, such as System.IO instead of System.Io.
•
Do not use abbreviations in identifiers or parameter names. If you must use abbreviations, use camel case for abbreviations that consist of more than two characters, even if this contradicts the standard abbreviation of the word.
Namespace Naming Guidelines •
You should use Pascal case for namespaces
•
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: Microsoft.Media Microsoft.Media.Design
2.1.6.5
Class Naming Guidelines •
Use a noun or noun phrase to name a class.
•
Use Pascal case.
Page 7 of 42
Good Programming Practices •
Use abbreviations sparingly.
•
Do not use a type prefix, such as C for class, on a class name. For example, use the class name FileStream rather than CFileStream.
•
Do not use the underscore character (_).
•
Where appropriate, use a compound word to name a derived class. The second part of the derived class's name should be the name of the base class. Example : ApplicationException public class FileStream public class Button public class String
•
Do not use the base word to access base class members unless you wish to resolve a conflict with subclasses member of the same name or when invoking base class constructors. public class Dog { public Dog ( string name ) {} virtual public void Bark ( int howLong ) {} } public class GermanShepherd : Dog { public GermanShepherd( string name ) : base( name ) {} override public void Bark( int howLong ) { base.Bark( howLong ) } }
2.1.6.6
Interface Naming Guidelines The following rules outline the naming guidelines for interfaces: • Name interfaces with nouns or noun phrases, or adjectives that describe behaviour. Example: IComponent // noun. ICustomAttributeProvider //noun. IPersistable //adjective.
•
Use Pascal case.
Page 8 of 42
Good Programming Practices •
Use abbreviations sparingly.
•
Prefix interface names with the letter I, to indicate that the type is an interface.
•
Use similar names when you define a class/interface pair where the class is a standard implementation of the interface. The names should differ only by the letter I prefix on the interface name.
•
Do not use the underscore character (_).
•
Always user Interfaces.
Example: The following are examples of correctly named interfaces. public interface IServiceProvider public interface IFormatable
The following code example illustrates how to define the interface IComponent and its standard implementation, the class Component.
public interface IComponent { // Implementation goes here. } public class Component: IComponent { // Implementation code goes here. }
2.1.6.7
•
Classes and interfaces should have at least 2:1 ratio of methods to properties.
•
Avoid interfaces with one method.
•
No more than 20 members per interface.
•
Avoid event as interface members.
•
Avoid abstract methods, user interfaces instead.
Attribute Naming Guidelines
You should always add the suffix Attribute to custom attribute classes. Example: public class ObsoleteAttribute{}
Page 9 of 42
Good Programming Practices
2.1.6.8
Enumeration Type Naming Guidelines
•
The enumeration (Enum) value type inherits from the Enum Class. O Use Pascal case for Enum types and value names. O
Use abbreviations sparingly.
O
Do not use an Enum suffix on Enum type names.
O
Use a singular name for most Enum types, but use a plural name for Enum types that are bit fields.
O
Always add the FlagsAttribute to a bit field Enum type.
Example: Good: public enum Color { Red, Green, Blue } Not Good: Public enum Color { Red = 1, Green = 2, Blue = 3 } Specifying type for an enum Not Good: public enum Color : long { Red, Green, Blue }
2.1.6.9
Static Field Naming Guidelines
•
Naming guidelines for static fields: O
Use nouns, noun phrases, or abbreviations of nouns to name static fields.
O
Use Pascal case.
O
Do not use a Hungarian notation prefix on static field names.
O
It is recommended that you use static properties instead of public static fields whenever possible.
Example: public static int Global1 = 100; public static int Global1 = 200;
Page 10 of 42
Good Programming Practices 2.1.6.10 Parameter Naming Guidelines It is important to carefully follow these parameter naming guidelines because visual design tools that provide context sensitive help and class browsing functionality display method parameter names to users in the designer.
•
Use camel case for parameter names.
•
Use descriptive parameter names. Parameter names should be descriptive enough that the name of the parameter and its type can be used to determine its meaning in most scenarios.
•
Use names that describe a parameter's meaning rather than names that describe a parameter's type. Development tools should provide meaningful information about a parameter's type. Therefore, a parameter's name can be put to better use by describing meaning. Use type-based parameter names sparingly and only where it is appropriate.
•
Do not use reserved parameters.
•
Do not prefix parameter names with Hungarian type notation. Example: correctly named parameters Type GetType(string typeName)
string Format(string format, object[] args)
2.1.6.11 Method Naming Guidelines The following rules outline the naming guidelines for methods: •
Use verbs or verb phrases to name methods.
•
Use Pascal case
Examples: Correctly named methods. RemoveAll() GetCharArray() Invoke()
2.1.6.12 Property Naming Guidelines The following rules outline the naming guidelines for properties: •
Use a noun or noun phrase to name properties.
•
Use Pascal case.
•
Do not use Hungarian notation.
Page 11 of 42
Good Programming Practices
•
Consider creating a property with the same name as its underlying type.
Example: Good: 1)
2)
public class smapleClass { public color BackColor { //Code for get and set assessors goes here. } } Providing a property with the same name as a type. public enum Color { // Insert code for Enum here. } public class Control { public Color Color { get {// Insert code here.} set {// Insert code here.} } }
Not Good: public enum Color { // Insert code for Enum here. } public class Control { public int Color { get {// Insert code here.} set {// Insert code here.} } } This is incorrect because the property Color is of type Integer.
2.1.6.13 Event Naming Guidelines The naming guidelines for events: • Use Pascal case. •
Do not use Hungarian notation.
Page 12 of 42
Good Programming Practices •
Use an EventHandler suffix on event handler names.
•
Specify two parameters named sender and e. o
The sender parameter represents the object that raised the event. The sender parameter is always of type object.
o
The state associated with the event is encapsulated in an instance of an event class named e. Use an appropriate and specific event class for the e parameter type.
•
Name an event argument class with the EventArgs suffix.
•
Consider naming events with a verb. For example, correctly named event names include Clicked, Painting, and DroppedDown.
•
Do not use a prefix or suffix on the event declaration on the type. For example, use Close instead of OnClose.
Example: Illustrates an event handler with an appropriate name and parameters. public delegate void MouseEventHandler(object sender, MouseEventArgs e);
Example: Illustrates a correctly named event argument class. public class MouseEventArgs : EventArgs { int x; int y; public MouseEventArgs(int x, int y) { this.x = x; this.y = y; } public int X { get { return x; } } public int Y { get { return y; } } }
2.1.6.14 Delegate Naming Guidelines The naming guidelines for events: •
Use Pascal case.
•
Do not use Hungarian notation.
•
Copy a delegate to local variable before publishing to avoid concurrency race condition.
•
Always check a delegate for null before invoking it.
•
Use delegate inference instead of explicit delegate instantiation
Page 13 of 42
Good Programming Practices
delegate void SomeDegate(); public void SomeMethod() { //Code goes here. } SomeDelegate someDelegate = SomeMethod; 2.1.7
USE EARLY BINDING FOR BETTER PERFORMANCE •
Early Binding provides much better performance than late binding.
•
Always specify a data type for variables when they are declared. This provides strong typing of variables for best performance. Good: Early Binding String[] address = null; Not Good: Late Binding Object[] address = null;
2.1.8
RETURNING VALUES Try to make the structure of your program match the intent. Example: if(booleanExpression) { return true; } else { return false; } should instead be written as return booleanExpression; Similarly, if(condition) { return x; } return y; should be written as return ((condition) ? x : y);
Page 14 of 42
Good Programming Practices
2.1.9
STATEMENTS
2.1.9.1
Simple Statements
argv++; /* Correct */ argc--; /* Correct */ argv++; argc--; /* AVOID! */
2.1.9.2
Compound Statements Compound statements are statements that contain lists of statements enclosed in braces "{ statements }".
2.1.9.3
•
The enclosed statements should be indented one more level than the enclosing braces.
•
The opening brace should be at the beginning of the line that precedes the compound statement; the closing brace should begin a line and be indented to the opening brace.
•
Braces are used around all statements, even single statements, when they are part of a control structure, such as an if-else or for statement. This makes it easier to add statements without accidentally introducing bugs due to forgetting to add braces. if, if-else, if else-if else Statements The if-else class of statements should have the following form: if(condition) { statements; } if(condition) { statements; } else { statements; } if(condition) { statements; } else if(condition) { statements; } else { statements; }
Page 15 of 42
Good Programming Practices
Note: if statements always use braces {}. Avoid the following error-prone form: if(condition) /* AVOID! THIS OMITS THE BRACES {}! */ statement;
2.1.9.4
for Statements A for statement should have the following form: for(initialization; condition; update) { statements; }
An empty for statement (one in which all the work is done in the initialization, condition, and update clauses) should have the following form: for(initialization; condition; update) { /* No Body */ }
When using the comma operator in the initialization or update clause of a “for” statement, avoid the complexity of using more than three variables. If needed, use separate statements before the “for” loop (for the initialization clause) or at the end of the loop (for the update clause). 2.1.9.5
while Statements A while statement should have the following form: while(condition) { statements; } An empty while statement should have the following form: while(condition) { /* No Body */ }
Page 16 of 42
Good Programming Practices 2.1.9.6
do-while Statements A do-while statement should have the following form: do { statements; } while(condition);
2.1.9.7
switch Statements •
Never use goto unless in a switch statement fall through.
•
Always have a default case in a switch statement that asserts.
A switch statement should have the following form: switch(condition) { case ABC: statements; /* falls through */ case DEF: statements; break; case XYZ: statements; break; default: statements; break; }
2.1.9.8
Using Statement Works with any IDisposable object Data access classes, streams, text readers and writers, network classes, etc.
Page 17 of 42
Good Programming Practices
Good: using (Resource res = new Resource()) { res.DoWork(); } Not Good: Resource res = new Resource(...); try { res.DoWork(); } finally { if (res != null) ((IDisposable)res).Dispose(); }
2.1.10 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: void SavePhoneNumber ( string phoneNumber ) { // Save the phone number. } Not Good: // This method will save the phone number. void SaveData ( string phoneNumber ) { // Save the phone number. }
2.1.11 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.
Page 18 of 42
Good Programming Practices
Good: // 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 Good: // Save address and send an email to the supervisor to inform that the address is updated. 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. // ... }
2.1.12 PAY ATTENTION TO INITIALIZATION ORDER The C# language adds the concept of initializes on member variable declarations •
These initializes get executed before the body of the constructor gets executed.
•
Variable initializes get executed before the base class's constructor gets executed.
•
Do not initialize variable of base class data; the base class has not yet been constructed.
2.1.13 DO NOT HARDCODE NUMBERS. •
Use constants instead.
Page 19 of 42
Good Programming Practices
Good: const int index = 5; Not Good: int index = 5;
2.1.14 STRING CONCATENATION • •
When building a log string, use StringBuilder, not string. Using StringBuilder instead of string will increase the performance.
Example: For string Good : StringBuilder address = new StringBuilder(); Address.Append( “Bangalore”); Address.Append( “Delhi”); Not Good: string address = null; address = “Bangalore” address += “Delhi”;
2.1.15 AVOID USING MANY MEMBER VARIABLES. Declare local variables and pass it to methods instead of sharing a member variable between methods. If you share a member variable between methods, it will be difficult to track which method changed the value and when. 2.1.16 USE ENUM WHEREVER REQUIRED. Do not use numbers or strings to indicate discrete values. Good: enum MailType { Html, PlainText } void SendMail (string message, MailType mailType) { switch ( mailType ) { case MailType.Html: // Do something break; case MailType.PlainText: // Do something break; default: // Do something break; } }
Page 20 of 42
Good Programming Practices
Not Good: void SendMail (string message, string mailType) { switch ( mailType ) { case "Html": // Do something break; case "PlainText": // Do something break; default: // Do something break; } }
2.1.17 DO NOT MAKE THE MEMBER VARIABLES PUBLIC OR PROTECTED. Keep them private and expose public/protected Properties. Good: public string CurrentUserName { Get {} Set {} } Not Good: Public string CurrentUser = null;
2.1.18 NEVER HARDCODE A PATH OR DRIVE NAME IN CODE. Get the application path programmatically and use relative path. Good: String FilePath = Application.StartupPath + “\test.txt”; Not Good: String FilePath = “C:\foldername\test.txt”;
2.1.19 "SELF CHECK" WHEN THE APPLICATION STARTSUP. "Self check" and ensure all required files and dependencies are available in the expected locations. Check for database connection in start up, if required. Give a friendly message to the user in case of any problems.
Page 21 of 42
Good Programming Practices 2.1.20 ERROR MESSAGES SHOULD HELP THE USER TO SOLVE THE PROBLEM. Give specific error messages like Good: "Failed to update database. Please make sure the login id and password are correct." Not Good: "Error in Application", "There is an error" etc.
2.1.21 AVOID COMMENTING EVERY LINE OF CODE. 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 many comments.
•
Less line of comments will make the code more elegant.
•
But if the code is not clean/readable and there are less comments, that is worse.
•
Do spells check on comments and also make sure proper grammar and punctuation is used.
If you have to use some complex or wired logic for any reason, document it very well with sufficient comments.
2.1.22 EXCEPTION HANDLING The following rules outline the guidelines for raising and handling errors: • All code paths that result in an exception should provide a method to check for success without throwing an exception. •
Never do a 'catch exception and do nothing'.
•
If you hide an exception, you will never know if the exception happened or not.
•
End Exception class names with the Exception suffix.
Example: public class FileNotFoundException : Exception { // Implementation code goes here. }
Page 22 of 42
Good Programming Practices •
Do not use exceptions for normal or expected errors, or for normal flow of control.
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: void ReadFromFile ( string fileName ) { try { // read from file. } catch ( FileIOException ex ) { // log error. // re-throw exception depending on your case. throw; } } Not Good: 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 ""; } }
2.1.23 MEMORY MANAGEMENT • •
Avoid Finalize() Only use Finalize() with Dispose() public void Dispose() { // Clean up unmanaged resources GC.SuppressFinalize( this ); } protected override void Finalize() { // Clean up unmanaged resources base.Finalize(); }
Page 23 of 42
Good Programming Practices
2.1.24 DATA ACCESS •
Accessing Relational Data o Always use the optimal Managed Provider o
Pick DataReader over DataSet when possible
o o
When the data will be remoted (disconnected) to the client or a remote application or component – for example, when using a Web Service that returns a Dataset. When more than one set of rows must be stored (and optionally the relationships between them).
o
Used stored procedures when possible
o
Always wrap your stored procedures in high level, type safe class. Only that class invokes the stored procedure.
o
Avoid putting any logic inside a stored procedure.
o
Always use type – safe data sets or data tables.
// Fill the DataSet with each DataAdapter daCustomers.Fill( dataSet, "Customers" ); daInvoices.Fill( dataSet, "Invoices" ); // Show the customer name Console.WriteLine( dataSet.Customer[0].FirstName ); Console.WriteLine( dataSet.Customer[0].LastName );
Always use transactions when accessing a database.
o
Always use Enterprise Services transactions
DO NOT use ADO.Net transactions.
[ Transaction(TransactionOption.Required)] public class Account : ServicedComponent { [ AutoComplete] // Code goes here }
Page 24 of 42
Good Programming Practices
o
Always use transaction isolation level set to serializable.
o
Avoid SQL Server Authentication
•
Use Windows Authentication instead.
Use SQL (TDS) classes for Data Access There are two sets of objects for accessing a data source: o
Objects prefixed OleDb (from the System.Data.OleDb namespace) use an OLE-DB provider to access that data store.
o
Objects prefixed Sql (from the System.Data.SqlClient namespace) use the Microsoft SQL Server Tabular Data Stream (TDS) interface to access that data store. The Sql prefixed objects are much faster and more efficient, and should always be used where you know that the data store will be Microsoft SQL server 7, 2000 or above.
o
•
XML Data o Use the XmlDataDocument for XML/DataSet integration o DOM è DataSet è DOM o Don’t use DOM if you don’t need it o Only necessary for in-memory editing o XmlReader is faster than DOM
2.1.25 SERIALIZATION •
Always mark non-sealed classes as serializable.
•
Always mark un-serializable member variables as non-serializable.
•
Always mark delegates on a serialized class as non - serializable fields.
Example:
[Serializable] public class MyClass { [field:NonSerialized] public event EventHandler MyEvent; }
Page 25 of 42
Good Programming Practices 2.1.26 MULTITHREADING •
Use synchronization Domains. Avoid manual synchronization because that offen leads to deadlocks and race conditions.
•
Never call outside your synchronization domains.
•
Always name your threads. Name is traced in the debugger Threads window, making a debug session more productive.
Example: Thread currentThread = Thread.CurrentThread; string threadName = “Main UI Thread”; currentThread.Name = threadName; •
Do not call Suspend() or Resume() on a thread.
•
Do not call Thread.Sleep(). o o
Thread.Sleep(0) is acceptable optimization technique to force a context switch. Thread.Sleep() is acceptable in testing or simulation code.
•
Do not call Thread.SpinWait().
•
Do not call Thread.Abort() to terminate threads. o
Use a synchronization object instead to signal the thread to terminate.
•
Avoid explicitly setting thread priority to control execution.
•
Do not read the value of the ThreadState property. o
•
Use Thread.IsAlive() to determine whether the thread is dead.
Do not rely on setting the thread type to background thread for application shutdown. o
Use a watchdog or other monitoring entity to deterministically kill threads.
•
Do not uses thread local storage unless thread affinity is guaranteed.
•
Do not call Thread.MemoryBarrier().
•
Never call Thread.Join() without checking that you are not joining your own thread.
Page 26 of 42
Good Programming Practices
void WaitForThreadToDie(Thread thread) { Debug.Assert(Thread.CurrentThread.GetHashCode() != thread.GetHashCode()); thread.Join(); }
Always use the lock () statement rather than explicit Monitor manipulation. •
Always encapsulate the lock () statement inside the object it protects. public class MyClass { public void DoSomething() { lock (this) {...} } }
•
Avoid fragmented locking.
•
Avoid using a Monitor to wait or pulse objects. Use manual or auto-reset events instead.
•
Do not use volatile variables. Lock your object or fields instead to guarantee deterministic and thread-safe access. o
•
Do not use Thread.VolatileRead(), Thread.VolatileWrite() or the volatile modifier.
Never stack lock statements because that does not provide atomic locking. Use WaitHandle.WaitAll() instead.
MyClass obj1 = new MyClass(); MyClass obj2 = new MyClass(); MyClass obj3 = new MyClass(); //Do not stack lock statements lock(obj1) lock(obj2) lock(obj3) { obj1.DoSomething(); obj2.DoSomething(); obj3.DoSomething(); }
Page 27 of 42
Good Programming Practices
2.1.27 REMOTING •
Prefer administrative configuration to programmatic configuration.
•
Always implement IDisposable on single call objects.
•
Always prefer TCP channel and binary format when using remoting. o
•
Unless a firewall is present.
Always provide a null lease for a singleton object.
public class MySingleton : MarshalByRefObject { public override object InitializeLifetimeService() { return null; } }
•
Always provide a sponsor for a client activated object. The sponsor should return the initial lease time.
•
Always unregister the sponsor on client application shutdown.
•
Always put remote objects in class libraries.
•
Avoid using SoapSuds.
•
Avoid hosting in IIS.
•
Avoid using uni-directional channels.
•
Always load a remoting configuration file in Main() even if the file is empty, and the application does not use remoting.
•
Allow the option of remoting some types later on, post deployment, and changing the application topology. static void Main() { RemotingConfiguration.Configure("MyApp.exe.config"); /* Rest of Main() */ }
Page 28 of 42
Good Programming Practices
•
Avoid using Activator.GetObject() and Activator.CreateInstance() for remote objects activation. Use new instead.
•
Always register port 0 on the client side, to allow callbacks.
•
Always elevate type filtering to full on both client and host to allow callbacks. Host Config file:
<serverProviders> <serverProviders> Client Config file:
<serverProviders>
Page 29 of 42
Good Programming Practices
2.1.28 ENTERPRISE SERVICES •
Do not catch exceptions in a transactional method. Use the AutoComplete attribute.
•
Do not call SetComplete(), SetAbort(), and the like. Use the AutoComplete attribute. [Transaction] public class MyComponent : ServicedComponent { [AutoComplete] public void MyMethod(long objectIdentifier) { GetState(objectIdentifier); DoWork(); SaveState(objectIdentifier); } }
•
Always override CanBePooled and return true (unless you have a good reason not to return to pool) public class MyComponent : ServicedComponent { protected override bool CanBePooled() { return true; } }
•
Always call Dispose() explicitly on a pooled objects unless the component is configured to use JITA as well.
•
Never call Dispose() when the component uses JITA.
•
Always set authorization level to application and component.
•
Set authentication level to privacy on all applications. [assembly: ApplicationActivation(ActivationOption.Server)] [assembly: ApplicationAccessControl(true, //Authorization AccessChecksLevel=AccessChecksLevelOption.ApplicationComponent, Authentication=AuthenticationOption.Privacy, ImpersonationLevel=ImpersonationLevelOption.Identify)]
Page 30 of 42
Good Programming Practices
• •
•
Set impersonation level on client assemblies to Identity. Always set ComponentAccessControl attribute on serviced components to true o The default is true. [ComponentAccessControl] public class MyComponent : ServicedComponent {...} Always add to the Marshaler role the Everyone user [assembly: SecurityRole("Marshaler",SetEveryoneAccess = true)]
•
Apply SecureMethod attribute to all classes requiring authentication. [SecureMethod] public class MyComponent: ServicedComponent {...}
2.1.29 SECURITY •
Code Access Security o Access to a protected resource o The ability to perform a protected operation
FileIOPermission permission = new FileIOPermission(PermissionState.None); permission.AllLocalFiles = FileIOPermissionAccess.Read;
•
Role-Based Security o Imperative (old way) public void DoTransaction() { IPrincipal principal = Thread.CurrentPrincipal; if (!principal.IsInRole("Managers")) { throw new SecurityException("Not a " + "manager!"); } // OK, do the transaction... }
o
Imperative (new way)
Page 31 of 42
Good Programming Practices
public void DoTransaction() { PrincipalPermission permission = new PrincipalPermission(null, "Managers"); permission.Demand(); // Now do the transaction... } o
Declarative
[PrincipalPermission(SecurityAction.Demand, Role="Managers")] void DoTransaction() { // this time, really // do the transaction... }
•
Apply encryption and security protection on application configuration files.
•
Do not use the /unsafe switch of TlbImp.exe. Wrap the CCW in managed code so that you could assert and demand permissions declaratively on the wrapper.
•
On client machine, deploy a security policy which grants client application only the permissions to call back the server and to potentially display user interface.
•
Client application identified by strong name.
•
Always refuse at the assembly level all permissions not required to perform the task at hand.
Page 32 of 42
Good Programming Practices
2.2
ASP.NET
The designers of Microsoft® ASP.NET have done an excellent job in preserving backward compatibility with ASP applications; you need to be aware of a few best practices as follows. In ASP, you can declare subroutines and global variables in between your code delimiters. <% Dim X Dim str Sub MySub() Response.Write "This is a string." End Sub %> In ASP.Net (Old Way) <script language = "vb" runat = "server"> String str; int x,y; private int add( int I, int j ) { return ( i + j ) } In ASP.Net (Best Way) in code behind we have to write like this private string str; int i, j; private int add( int i, int j ) { return ( i + j ); }
2.2.1
DISABLE SESSION STATE WHEN NOT IN USE •
Not all applications or pages require per-user session state. If it is not required, disable it completely. <%@ Page EnableSessionState="false" %>
Page 33 of 42
Good Programming Practices 2.2.2
CHOOSE YOUR SESSION STATE PROVIDER CAREFULLY •
ASP.NET provides three distinct ways to store session data Option Inproc StateServer SqlServer Off
2.2.3
Description Session state is stored locally on this server (ASP style). Session state is stored in a state service process located remotely or potentially locally. Session state is stored in a SQL Server database. Session state is disabled.
In-proc session state is by far the fastest solution. If you are only storing small amounts of volatile data in session state you should use the in-process provider.
Out-of-process solutions are primarily useful in Web garden and Web farm scenarios or in situations in which data cannot be lost in the event of a server/process restart.
USAGE OF VIEWSTATE •
Depending on the size of the viewstate, transmitting it across a network can entail a performance hit.
•
To disable viewstate maintenance for a page, use the following Page directive: <%@Page EnableViewState=”false” … %>
•
To disable viewstate maintenance for a single control, use the EnableViewState property:
•
To disable viewstate maintenance for an entire application, change the setting in web.config: <Pages enableviewstate=”false” …/>
Control Level disabling the View state: Page Level disabling the View state: <%@ Page EnableViewState="false" %>
2.2.4
AVOID EXCESSIVE ROUND TRIPS TO THE SERVER •
2.2.5
Validating form entries can often take place on the client before the user submits data.
USE PAGE.ISPOSTBACK TO AVOID EXTRA WORK ON A ROUND TRIP • Handle server control postbacks. void Page_Load(Object sender, EventArgs e) { // ...set up a connection and command here... if (!Page.IsPostBack) { String query = "select * from Authors where FirstName like '%JUSTIN%'"; myCommand.Fill(ds, "Authors"); myDataGrid.DataBind(); } }
Page 34 of 42
Good Programming Practices
•
2.2.6
Whether there is an initial request for the page or a response to a server control event.
REUSE WORK BY CACHING • • •
Dynamic response generated by a request use output cache. Caches portions of a response generated by a request use fragment cache. Caches portions of a response generated by a request use data cache.
CAUTION: When you use the @ OutputCache directive, the Duration and VaryByParam attributes are required. If you do not include them, a parser error occurs when the page is first requested.
2.2.7
2.2.8
REMOVE CODE FROM CONTENT AS MUCH AS POSSIBLE •
Separate your code from HTML content.
•
Clean up functions that mix code and script throughout a function body.
NEW PAGE DIRECTIVES •
You can have as many lines of directives as you need.
•
Directives may be located anywhere in your .aspx file.
•
Standard practice is to place them at the beginning of the file.
<%@ Page Language="vb" CodeBehind="test.aspx.vb"%> <%@QutputCache Duration="60" VaryByParam="none" %>
2.2.9
USE RESPONSE.WRITE FOR STRING CONCATENATION •
Use the HttpResponse.Write method in your pages or user controls for string concatenation. o This method offers buffering and concatenation services that are very efficient.
•
Using multiple calls to Response.Write, is faster than concatenating a string with a single call.
Response.Write("a"); Response.Write(myString); Response.Write("b"); Page 35 of 42
Response.Write(myObj.ToString());
Good Programming Practices
2.2.10 DO NOT FORGET TO DISABLE DEBUG MODE •
Debug mode degrades performance significantly.
•
Disable Debug mode before you deploy a production application or measure performance
2.2.11 USE SERVER CONTROLS ONLY WHEN APPROPRIATE •
When the element is only used to run some client-side script. Example: A button that opens a new browser window or that interacts with a client-side ActiveX control or java applet, or that calculates some value for display in the page using DHTML or an alert dialog.
•
When the element is a hyperlink that opens a different page or URL and there is no need to process the values for the hyperlink on the server.
•
Any other times when access to the element’s properties, methods or events in server-side code is not required.
2.2.12 USE THE WEB.CONFIG/MACHINE.CONFIG FILE TO STORE APPLICATION WIDE DATA •
While you can use the web.config file to store your database connection string it is more secure to store the database connection string in the File / machine.config file.
•
It is very easy to retrieve this information from any of your ASP.NET application. string connstring = null; connstring = ConfigurationSettings.AppSettings("ConnectionString"); To add support for this to your Web.Config, you need to add an AppSettings section, like this one:
•
Server that has many different sites all using the same database (i.e. different IP addresses and domain names, not subwebs), the best solution if you want to keep all of the connection information in one place is to use the machine.config file for the server.
Page 36 of 42
Good Programming Practices 2.2.13 EXCEPTION HANDLING IN ASP.NET The various options available for us for handling errors in asp.net are: •
Code in such a way that possibility of error is less. o
•
Trapping errors as and when they occur. o
•
Try to avoid error conditions as far as possible
You should be prepared to handle unexpected conditions like network failures, database crashes and the like.
public class TextReader { public string ReadLine() { try { // Read a line from the stream. } catch ( NullReferenceException NRE ) { Response.Write("Null Reference Exception
"); } catch ( Exception DefaultExcep ) { Response.Write("Error Occured, Unknown Orgin"); } finally { Response.Write("Caught Exception"); } } errors wherever appropriate to assist unified error handling. Throw } o Brings the uniformity in the error handling techniques. o
We can identify business error conditions in code itself rather than relaying on some database stored error codes.
o
We can provide much detailed and user-friendly information to the users.
public class TextReader { public string ReadLine() { try { // Read a line from the stream. } catch (Exception e) { throw new IOException ("Could not read from stream", e); } Caution: } Throwing an exception is an expensive task. }
Page 37 of 42
Good Programming Practices
•
Handle errors via Application_Error event in global.asax. o
Application_Error event that gets fired every time an unhandled exception occurs in the web application.
protected void Application_Error(Object sender, EventArgs e) { Response.Write("Unexpected error occured !
" & Server.GetLastError().Message); Response.End(); } Note: This event will be fired only for unhandled exceptions •
Provide custom error pages for predefined IIS errors. o
This provides much better user experience by the use of custom error pages.
o
The Page class has a property called as ErrorPage that can contain url of a HTML/ASPX page that will be displayed if any unhandled exception occurs in your code. Mywebform.ErrorPage="errpage.aspx"
o
In the web.config file, turn on the customErrors mode as shown below: <customErrors mode="On" />
•
Provide custom error pages for business errors. o o
This you can customize error pages without going to IIS snap-in and also change them easily in future Modify <customErrors> section of web.config file.
<customErrors mode="On" defaultRedirect="errpage.aspx"> <error statusCode="404" redirect="filenotfound.aspx" />
2.3
GENERAL TIPS •
The engineer who wrote the code is mostly responsible for QA.
Page 38 of 42
Good Programming Practices •
No code is finished until the unit test is finished.
•
Good design results from getting something going quickly, followed by lots of quick iteration.
•
The best architectures evolve for a long time.
•
Code reviews are a regular part of development.
•
Difficult parts of code are best done by a team of two people working closely together.
•
If you see a new bug while doing something else, drop everything and jump on the chance to fix it. It just makes your life easier down the road.
•
It is possible to eliminate all bugs from many subsystems and you should use techniques that make it possible, like Monte Carlo simulations and exhaustive checking code whenever possible.
•
Complexity is your enemy – the sooner you learn the smell of too much complexity, the better.
•
Simplicity is your friend. Don’t fall for a really cool fancy algorithm unless you know you need it.
•
Don’t build elaborate systems unless you know you need them.
•
Research existing open source project to make sure the code you need isn’t already available.
•
Never write a slow program.
•
By the time your project is finished your competitor’s project will be way better than you think.
•
Price is only one feature of software. It’s often not the most important. From the user’s perspective, simple things should be simple and complex things should be possible.
•
Automate releases.
•
Use source level debuggers.
•
Use IDEs
•
Don’t write makefiles unless you have too.
•
Measures then optimize.
• •
Always spend at least 10% of your time learning new things. Use multiple languages, programming environments, operating systems and programming methodologies until you are comfortable with them and learn to appreciate the benefits of different approaches.
•
Use the best tools, even if they are expensive.
•
Optimize variable names for maximum clarity, not minimum typing.
Page 39 of 42
Good Programming Practices •
It’s OK to make mistakes. The only thing that isn’t OK is to not learn from them.
•
Don’t leave code commented out unless it’s clearly explained.
•
Approach each piece of code you write like its final finished code. Don’t leave loose ends to clean up later because you’ll never have time to clean them up.
•
Never code an algorithm that you know won’t work in the final product either because it won’t scale, run fast enough, or will have a rare failure case.
•
All successful products have a life longer than you can conceive. Files and processors will grow by factors of 10,000 – make sure your design can accommodate change.
•
Don’t code the first algorithm that comes to mind. Try to examine all possible approaches and choose the best. Get someone to review your approach before coding.
•
Don’t put band aids on bad code, rewrite it. At first is seems hard, but after you’ve done it awhile you’ll find successive rewrites go much faster than you thought.
•
When you work on someone else’s code, don’t leave it in worse shape than it came to you. Ask the original author to review your change.
•
No bug is impossible to fix.
•
Get to be good friends with a programmer, designer, or writer who’s better than you.
•
The Code should be o o o
consistent and predictable scalable, high-performance secure
Page 40 of 42