Monday, October 18, 2010

Coding Techniques and Programming Practices in DOTNET(C#)

Coding Techniques and Programming Practices in DOTNET(C#)

Coding standards increase productivity in the development process, make code easier to maintain, and keep code from being tied to one person or team.

A comprehensive coding standard encompasses all aspects of code construction. While developers should prudently implement a standard, it should be adhered to whenever practical. Completed source code should reflect a harmonized style, as if a single developer wrote the code in one session.

Using solid coding techniques and good programming practices to create high-quality code plays an important role in software quality and performance. In addition, if you consistently apply a well-defined coding standard, apply proper coding techniques, and subsequently hold routine code reviews, a software project is more likely to yield a software system that is easy to comprehend and maintain.


Coding Techniques
Coding techniques incorporate many facets of software development. Although they usually have no impact on the functionality of the application, they contribute to an improved comprehension of source code. All forms of source code are considered here, including programming, scripting, markup and query languages.
The coding techniques defined here are not proposed to form an inflexible set of coding standards. Rather, they are meant to serve as a guide for developing a coding standard for a specific software project.
The coding techniques are divided into three sections:
·         Names
·         Comments
·         Format

I. Names
The naming scheme is one of the most influential aids to understanding the logical flow of an application. A name should tell "what" rather than "how." By avoiding names that expose the underlying implementation, which can change, you preserve a layer of abstraction that simplifies the complexity
Note:
Pascal Casing - First character of all words are Upper Case and other characters are lower case.
Camel Casing - First character of all words, except the first word are Upper Case and other characters are lower case.


  • 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;
                        ...
            }
}

  • 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++ )
{
            ...
}


If the variable is used only as a counter for iteration and is not used anywhere else in the loop, many people still like to use a single char variable (i) instead of inventing a different suitable name.

- Do not use underscores (_) in variable names.
- Namespace names should follow the standard pattern

<company name>.<product name>.<top level module>.<bottom level module>
  • File name should match with class name

    For example, for the class
    HelloWorld, the file name should be helloworld.cs (or, helloworld.vb)

The prefixes to be used for declaring the variables are as follows:


DataType
Prefix
Example
sbyte  
sy
syVar
short  
s
sVar
int    
int
intVar
long   
lng
lngVar
byte   
byt
bytVar
ushort 
us
usVar
uint   
uint
uintVar
ulong  
ulng
ulngVar
float  
f
fVar
double 
dbl
dblVar
Decimal
dec
decVar
bool   
bln
blnVar
char   
chr
chrVar

Where Var is the Variable name to be used

Naming Conventions to be used for Databases are mentioned below.


Control
Prefix
Assembly                               
asm
Boolean                                
bln
Button                                 
btn
Char                                   
ch
CheckBox                               
chk
ComboBox                               
cmb
Container                              
ctr
DataColumn                             
dcol
DataGrid                               
dg
DataGridDateTimePickerColumn           
dgdtpc
DataGridTableStyle                     
dgts
DataGridTextBoxColumn                  
dgtbc
DataReader                             
dr
DataRow                                
drow
DataSet                                
ds
DataTable                              
dtb
DateTime                               
dt
Dialog                                 
dlg
DialogResult                           
dlgr
Double                                 
dbl
Exception                              
ex
GroupBox                               
gbx
HashTable                              
htbl
ImageList                              
iml
Integer                                
int
Label                                  
lbl
ListBox                                
lbx
ListView                                
lv
MarshallByRefObject                    
rmt
Mainmenu                               
mm
MenuItem                               
mi
MDI-Frame
mdframe
MDI-Sheet                              
mdsheet
NumericUpDown                          
nud
Panel                                  
pnl
PictureBox                             
pbx
RadioButton                            
rb
SDI-Form                               
frm
SqlCommand                             
objsqlcmd
SqlCommandBuilder                      
objsqlcmbd
SqlConnection                          
objsqlcon
SqlDataAdapter                         
objsqlda
StatusBar                              
stb
String                                 
str
StringBuilder                          
sb
TabControl                             
tbctl
TabPage                                
tbpg
TextBox                                
txt
ToolBar                                
tbr
ToolBarButton                          
tbb
Timer                                  
tmr
UserControl                            
usr
WindowsPrincipal                       
wpl
RadioButtonList
rbl
Array
arr
ArrayList
arrl
CheckBoxList
chkl
TreeView
trv
GridView
grdv
DataView
dv
DataColumn
dc
SqlDataSource
objsqlds
Repeater
rptr
ContextMenu
cmnu
Reports
rpt
ProgressBar
pgb
CrystalReport
crpt
Timer
tmr
MenuItem
mnuit
MaskEditControl
msked
ImageButton
imgbtn
LinkButton
lnkbtn
HyperLink
hyplnk
PictureBox
pbx
ImageBox
ibx
PlaceHolder
plh
Calendar
cal
Literal
ltrl
TextArea
txta
VerticalScrollBar
vscb
HorizontalScrollBar
hscb
CrystalReportViewer
crptv
NotifyIcon
nico
RequiredFieldValidator
rfv
CustomValidator
cv
RangeValidator
rv
RegularExpressionValidator
regv
CompareValidator
cmpv
ValidationSummary
vs
Pointer
ptr
Image
img
ImageMap
imgmp
Table
tbl
BulletedList
blst
HiddenField
hidtxt
Adrotator
adr
Fileupload
flupd
Wizard
wiz
Xml
xml
Multiview
mltv
DataList
dl
AccessDataSource
ads
XmlDataSource
xds
SiteMapDataSource
smpds
ReportViewer
rptv
SiteMapPath
smp
Menu
mnu
Login
lgn
LoginView
lgnv
PasswordRecovery
pwdr
LoginStatus
lngs
LoginName
lngn
ChangePassword
chpwd
WebPartManager
wpmgr
ProxyWebPartManager
pwpmgr
WebPartZone
wpz
CatalogZone
ctz
DeclarativeCatalogPart
dctp
PageCatalogPart
pgctp
ImportCatalogPart
imctp
EditorZone
edz
AppearanceEditorPart
apedp
BehaviourEditorPart
bedp
LayoutEditorPart
ledp
PropertyGridEditorPart
pgedp
ConnectionZone
conz
Input(Button)
btn
Input(Reset)
btn
Input(Submit)
btn
Input(Text)
txt
Input(File)
fl
Input(Password)
txt
Input(CheckBox)
chk
Input(Radio)
rb
Input(Hidden)
hidtxt
Select
cmb
HorizontalRule
hr
Div
div
CrystalReportPartsViewer
crptpv
CrystalReportSource
crpts
ColorDialog
clrd
ContextMenuStrip
cmnus
DataGridView
dgv
ErrorProvider
errp
EventLog
evtl
FileSystemWatcher
flsw
FlowLayoutPanel
flp
FolderBrowserDialog
flbd
FontDialog
fntd
HelpProvider
hlpr
LinkLabel
lnklbl
MaskedTextBox
msktxt
MenuStrip
mnustp
MessageQueue
msgq
MonthCalendar
cal
PrintDialog
prtd
PrintDocument
prtdoc
PrintPreviewControl
ppctrl
PrintPriviewDialog
ppd
StatusStrip
sts
TableLayoutPanel
tlpnl
ToolStrip
tls
ToolStripContainer
tlsc
ToolTip
ttp
TrackBar
tkb
WebBrowser
wbr
ReportDocument
rptdoc

Naming Conventions to be used for databases:

1.    A good table or field name...
a.    has a unique descriptive name that is meaningful to the entire organization
b.    accurately, clearly, and unambiguously identifies the table subject or field characteristic
c.    has the minimum number of words necessary to convey the table subject or field meaning
d.    does not contain unknown acronyms or abbreviations
e.    does not contains words that describe the physical characteristics, such as “file”, “record”, “data”, or “table”
f.     does not identify more than one table subject or field characteristic
g.    uses the singular form of the name
h.    Avoid Spaces:
      
Poor Choices
Better Choices
Sample Event
sampleEvent
Sample Id
sampleId
Site Id
siteId

i.      Avoid Special Characters:
Poor Choices
Better Choices
Site/Locations
siteLocation
phone #
phoneNum
%MossCover
mossCoverPercent
Rain?
isRaining
project$
projectCost

j.      Use Consistent Case:
Poor Choices
Better Choices
DIGITAL_PHOTO
digitalPhoto
SampleStartTime
sampleStartTime
WindSpeed
windSpeedmph

k.    Singularize Names
Poor Choices
Better Choices
lifeStages
lifeStage
numBirds
birdCount
observerNames
ObserverName

l.      Avoid Unknown Abbreviations
Poor Choices
Better Choices
smptrnid
sampleTransectId
WtrCod
waterCode
colforCur
isCollectedForCuration

m.   Limit Length

Poor Choices
Better Choices
waterQualityEvaluationCode
h2oQualityEvalCode
DescriptionOfGeomorphicDisturbance
geomorphicDisturbDesc
smptrnid
sampleTransectId

n.    Use Specific Names

Poor Choices
Better Choices
AmphibianSize
amphibianLengthIn
treeSize
treeHeightM
TreeName
treeSpeciesName -or- treeCommonName


o.    Ensure Single Values

Poor Choices
Better Choices
fullName
firstName, lastName
CityStateZip
cityName, stateCode, zipCode
observersInitials
observerInitials


p.    Avoid Calculations

Poor Choices
Better Choices
totalLineItemCost
itemQty, unitCost
plotAreaM2
plotWidthM, plotHeightM
eventDurationTime
eventStartTime, eventEndTime

q.    Avoid Reserved Words

Poor Choices
avg
count
currency
Date
Desc
Exists
Group
Index
Key
Max
Memo
Min
Name
Number
Order
Percent
Property
Section
Set
Sum
Time
Type
Value
Year


2.    Conventions to be followed for naming tables, fieldnames and Variable names are as follows:
a.    Table Name:
Use meaningful names that specify the purpose of the table.
Pascal Casing should be used for naming tables.
Ex. CustomerDetails.
b.    Stored Procedure:
Use names that imply the functionality carried out by the stored procedures.
Ex. For inserting details into a customers table, use names like CustomerInsert.
c.    Field Names:
Field Names must identify the purpose of the fields.
Ex. CustomerId, FirstName, LastName
d.    Variables:
Variable Names used in stored procedures must be in accordance with the field names used in tables.
Ex. @FirstName, @LastName


II. 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 comment.
  • Fewer 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 wired logic for any reason, document it very well with sufficient comments.
  • 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.
  • Does a spell check on comments and also make sure proper grammar and punctuation is used.
  • Spell checks comments.
  • Use descriptive style for comments.
  • Place comments immediately before the code they refer to, at the same indent level and with no intervening blank lines.
  • Use XML comments to document all public types and methods in a class library; use standard comments in all other cases
  • Avoid /*...*/ (block) comments.
  • Avoid statement-level comments (i.e. comments on same line).
  • Add a comment to mark the end if, for, while blocks longer than 20 statements.
  • Use single-line // comments to comment out code.


III. Format

Indentation and Spacing

  • Use TAB for indentation. Do not use SPACES.
  •   Comments should be in the same level as the code.
  •   Curly braces ( {} ) should be in the same level as the code outside the braces.
  •   Use one blank line to separate logical groups of code.

      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;
      }

This code looks better, than the code shown above:

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;
            }

  •   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 adn not in the same line as if, for etc.


Good:

if ( ... ) 
                        {
                                    // Do something
                        }

Not Good:

if ( ... )  {
                                    // Do something
                        }



   Spaces and indentation

a.    Indent code with spaces.
b.    Wrap lines longer than 80 characters.
c.    Wrap lines after commas and before operators.
d.    Don't wrap lines in the middle of a parenthesized expression.
e.    Don't wrap elements at the same logical nesting level.
f.     Place open/close curly braces in a line of their own, at the same indent level as the preceding statement.
g.    Place open curly braces at the end of the statement that defines the block.
h.    Add one space after an open parenthesis and before a close parenthesis, in if, while, for, foreach, switch, and lock statements.

Programming Practices
  • 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.
  • 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.
            }


  • 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:

            // 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.
                        // ...
            }

  • 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;

  • Do not hardcode numbers. Use constants instead.
  • Do not hardcode strings. Use resource files.
  • 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.
  • 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;
                        }
            }

  • 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:".
  • In the application start up, do some kind of "self check" and ensure all required files and dependancies 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.
  • If the required configuration file is not found, application should be able to create one with default values.
  • If a wrong value found in the configuration file, application should throw an error or give a message and also should tell the user what are the correct values.
  • Error messages should help the user to solve the problem. Never give error messages like "Error in Application", "There is an error" etc. Instead give specific messages like "Failed to update database. Please make sure the login id and password are correct."
  • 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.

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. 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.

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 "";                     
                        }
            }

·         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.
·         You can have an application level (thread level) error handler where you can handle all general exceptions. In case of an 'unexpected general error', this error handler should catch the exception and should log the error in addition to giving a friendly message to the user before closing the application, or allowing the user to 'ignore and proceed'.
·         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 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.





Conclusion

Coding standards are a must for any development team. They ensure consistency and simplify the maintenance process with legible code. Please make sure the standard is strictly followed in future projects.

1 comment: