PeterBox Homepage
Hallo, willkommen auf Singapur, 9.9.10 15:13:29

.NET Development Handbook

.NET Development Handbook .NET Development Handbook .NET Development Handbook .NET Development Handbook
.NET Development Handbook

4 .NET Framework

4.1.NET Namespaces
4.2.NET Solutions
4.3.NET Projects
4.3.1Setting of Project Properties
4.3.1.1Default Namespace
4.3.1.2Check for Arithmetic Overflow/Underflow
4.4File Folders
4.5C#
4.5.1Formatting Rules for C#
4.5.2Programming Hints
4.5.2.1Avoid negation in IF statements
4.5.2.2Use always && (conditional and) and || (conditional or)
4.5.2.3Start FOR loop counters with 0
4.5.3XML Comments for C#
4.6Classes
4.6.1Class Testing
4.7Methods
4.7.1Method Structure
4.7.2Method Testing
4.8Properties
4.9Enumerations
4.9.1Special Enumeration Identifiers
4.10Localisation / Globalisation
4.10.1Resource Files
4.11Exception Handling
4.11.1Custom Exceptions
4.11.2Problem Detection and Exception Throwing
4.11.3Exception Detection and Handling
4.12Multithreading
4.12.1Recommendations
4.13Remoting
4.14Debugging
4.15Tracing
4.15.1PBoxTracing
4.15.2Different behavior debug / relase build
4.15.3PBoxTracing Output Media
4.16Configuration
4.17Peer Review .NET Code

4.1 .NET Namespaces

Namespaces in .NET help to prevent name collisions, i.e. that two developers working on different projects uses exactly the same name. This would create a problem when the projects should ever be joined. .NET prevents this problem by adding a namespace to the names of programming elements.

PeterBox.com is a registered name and shouldn抰 be used by anyone else. By starting our namespace with PeterBox, we guarantee that our code can be combined with any software written anywhere else.

Each software project should have its own namespace allowing the use of the same name in different projects. The namespace name looks like this: PeterBox.ProjextX. Code that can be used also in other projects goes into the PeterBox.General namespace.

 

Namespace

Comment

PeterBox

Shouldn抰 be used directly

PeterBox.General

For project independent modules like libraries, lowest level database access, exception handling

PeterBox.Website

PeterBox.com website

4.2 .NET Solutions

A .NET Solution is a container for several .NET Projects. Its main use is for Visual Studio to provide a structured access to all .NET projects needed to create an application. It mostly contains projects and few project independent files. One solution might contain projects like:

• 1 ASP.NET website

• Several library .DLLs

• Several executable Windows programs (testing, utilities, ?

• Other files

4.3 .NET Projects

A .NET Project contains all files needed to create a piece of executable code, usually a .dll (dynamic-link library), an .exe file (executable program) or the files for an ASP.NET website. All files belonging to a specific application should be included in one project. Code that can be shared with other applications should be placed in their own .dll projects. Remove as much functionality from the user interface (.exe, ASP.NET) into .dlls, this allows separating of user interface from the other layers and reuse of the code in other projects.

There are also other projects types like for setup and deployment of the application, application center test projects, etc.

A project can contain any type of file and is the perfect place for documentation.

A Solution should contain at least the following projects:

 

Project

Description

Application Project

Windows or ASP.NET project

PeterBoxLibrary

.DLL with code used in various applications

ApplicationTest

Console application for running tests

Install

Setup and deployment of Application

4.3.1 Setting of Project Properties

4.3.1.1 Default Namespace

Make sure to change the Default Namespace property to the correct namespace, like PeterBox.Project, because Visual Studio uses the file name of the first item added to a project, which is not helpful.

4.3.1.2 Check for Arithmetic Overflow/Underflow

Set to true. We want to get an error message if one of these errors occure.

 

4.4 File Folders

Visual Studio creates a folder (windows file directory) containing all files of a .NET project. It creates additional sub folders to separate source code from executables, etc.

A project can contain code from different layers (e.g. user interface and business rules). Add an additional directory MiddleTier to keep the source code for these two layers separated.

Source files are stored in Visual SourceSafe, but they need to be copied (checked out) to a PC for development.

Make a directory on you home drive for all your .NET projects. Visual Studio will store all projects there, except Asp.NET projects, which are stored under: C:\Inetpub\wwwroot

4.5 C#

C# and Visual Basic are very similar and offer the same kind of functionality. Visual Basic has the easier understandable syntax and thanks to the immediate compilation whenever something is typed in, the better reporting of errors. In C#, a build has to be performed to get the same kind of error messages.

Nonetheless, C# is selected, because:

• Hides less from the developer than Visual Basic

• Close to C++ and Java

• Seems Microsoft抯 choice of language

• Advanced tools available (like XML comments)

4.5.1 Formatting Rules for C#

Additionally to the general rules listed in 3.2 Coding Format, the following formatting should be used for C#:

• Use 4 space for indentation (no tabs)

• Use 1 space before and after equal sign in assignment Index = 0

• Use // xxx xxxx 爁or one line comments

• Use /* xxx xxxx */ 爁or multi-line comments

• Use title comments to help with orientation in big listings. They can be used to group methods or to group lines of in long methods:

 

牋?code ...

 

 

牋?// Title

牋?// -----

 

牋?code ...

 

• Write the opening curly bracket on the same line as its definition (namespace, class, method, if(), ? preceded by one space.

• Properties can be written in the following condensed form:

 

牋?/// <summary>

牋?/// presently connected database

牋?/// </summary>

牋?public DBConnection DatabaseUsed {

牋牋牋?get {return _DatabaseUsed;}

牋?}

牋?private DBConnection _DatabaseUsed;

 

• Separate 2 methods be 2 empty lines

 

/*****************************************************************************

Sample Program

==============

 

Some comment might be here

 

Copyright (c) Peter Huber, Singapore, 2003

*****************************************************************************/

 

using System;

 

namespace PeterBox {

牋?/// <summary> Just a counter not telling anyone anything </summary>

牋?class Counter {

牋牋牋?int _Counter;

 

牋牋牋?

牋牋牋?/// <summary>

牋牋牋?/// Initialise counter, negative value means counting has not started yet

牋牋牋?/// </summary>

牋牋牋?public Counter(int start) {

牋牋牋牋牋? _Counter = start;

牋牋牋?}

 

 

牋牋牋?// Example of a Title

牋牋牋?// ------------------

 

牋牋牋?/// <summary> Increment counter by one </summary>

牋牋牋?void Increment() {

牋牋牋牋牋?if (_Counter<0) {

牋牋牋牋牋牋牋? // start counting

牋牋牋牋牋牋牋? _Counter=0;

牋牋牋牋牋?} else if (_Counter>=99) {

牋牋牋牋牋牋牋? // do nothing

牋牋牋牋牋?} else {

牋牋牋牋牋牋牋?// increment

牋牋牋牋牋牋牋? _Counter+= 1;

牋牋牋牋牋 爙

牋牋牋?}

牋?}

}

 

• See example above for proper if syntax, other control statements as follows:

 

switch (_Counter) {

牋?case 0:

牋牋牋? //starting

牋牋牋?break;

 

牋?case 99:

牋牋牋? //stopped

牋牋牋?break;

 

牋?default:

牋牋牋? //counting or undefined

牋牋牋?break;

牋?}

 

for (int Index = 0; Index<9; ++Index) {

牋?// do something

}

 

int Position=0;

while (Position<9) {

牋?++Position;

}

 

do {

牋?++Position;

} while (Position<10);

 

try {

牋?// do something risky

 

} catch (Exception e) {

牋?// handle exception

 

} finally {

牋?//relase resources

}

 

• If a method has many parameters, write one parameter per line:

 

public static void Ses_getStatistic(

牋?string Cookie,

牋?out int Sessions,

牋?out int Pages,

牋?out int Errors) {

 

• If a method with many long parameters is called, write one parameter per line:

 

Ses_getStatistic(

牋?"ahsfd ajkdhfjka dfjka dfkja kfjd akjsdfh akjshdf kjashdf lkja",

牋?out Sessions,

牋?out Pages,

牋?out Errors) {

 

• Order using statements alphabetically. List using statements referencing the .Net framework first and separate them from other using statements by one line.

 

using System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

 

using NUnit.Framework;

using PeterBox.General;

• Declare local variables just before the first code line referring them. If a local variable is used by different parts of a procedure, write the declaration right after the method parameters.

4.5.2 Programming Hints

The suggestions in this chapter improve code readability and help also to avoid programming errors.

4.5.2.1 Avoid negation in IF statements

Try to avoid negation in if conditions. This makes it difficult to understand code. Usually it抯 better to test for an error condition and treat the error immediately.:

 

Bad:

if (!error1) {

牋?if (!error2)

牋?牋牋// do processing as needed

牋?} else {

牋?// handle error 2

牋?}

} else {

牋?// handle error 1

}

 

Good:

if (error1) {

牋?// handle error 1

} else if (error2) {

牋?// handle error 2

} else {

牋?// do processing as needed

}

 

4.5.2.2 Use always && (conditional and) and || (conditional or)

If possible, use &&, which executes the second part of the and condition only if the first part is true. This is needed to avoid runtime error if the second part is defined only if the first part is true. However, this problem is not easy to detect and code using & instead of && will look like running correctly.

The & is only needed, if both parts of the and condition need to be evaluated, which is only the case if the second part changes some values (has side effects). In general, programming construct with side effects should be avoided anyway, because they are difficult to understand.

 

if ((Element==null) && (Element.Value==x)){

牋?// do something

}

4.5.2.3 Start FOR loop counters with 0

Try to be consistent how you write for loops. There are good reasons to start a loop counter with 0 or with 1. But it is confusing for other people reading your code, if you start sometimes with 0 and sometimes with 1. It might also be a source of errors, because the testing for the end condition is slightly difference, ?<?instead of ?lt;? It抯 very easy to get the ??wrong, but quite difficult to detect the error. If all loop counters start with 0, normally no ??is needed.

The recommendation is to start with 0, which makes especially sense for array access.

 

Bad:

for (int x = 1; x<=BorderPixels; ++x){

牋?// do processing as needed

}

 

Good:

for (int x = 0; x<BorderPixels; ++x){

牋?// do processing as needed

}

 

4.5.3 XML Comments for C#

Basically, XML comments would be a great tool for commenting source code. However, since this is the first version, it has some serious flaws:

• Visual Studio creates a warning for each member without summary description, if in project properties, configuration, build, XML documentation file is used.

• Comment cannot be placed on the same line after the member definition, only on a preceding line. This is annoying if there are many members.

• The build web comment tool in Visual Studio supports only few of the XML comments tags

• If parameters in a method are changed, the <param> ?lt;/param> don抰 get automatically updated.

• Still many bugs

However, there are some benefits using XML comments, especially using /// <summary> for classes and members. Visual studio will use this for hints. Hopefully, Visual Studio will improve in the future the support for XML comments.

4.6 Classes

4.6.1 Class Testing

Each class should have a method called Test:

 

static public bool Test(){}

 

The test method is used to verify that the class still works properly. It is run as part of every integration test. It returns true if every test succeeds, otherwise wrong. If possible, exceptions should be caught and reported by the test procedure. It should be the last method in a class.

4.7 Methods

4.7.1 Method Structure

• Comment

• Header. If there are many parameters in the method, put each parameter on its own line. ???sample

• If there are too many parameters, consider using a struct instead. Combine only logically related parameter in one struct or class.

• Test each parameter抯 range. During debugging, throw exception. In release code, substitute the illegal value by something meaningful, if possible, and write an error trace. If substitution is not possible, raise exception.

• Define local variables close to where they are used:

??? Sample

4.7.2 Method Testing

The test method should call every method of the tested class, including constructors and destructors. Make calls with valid and invalid parameters (value too low, minimum value, some reasonable values, maximum value, value too big). If a method changes the state of a class, make calls for every state in logical and illogical order.

4.8 Properties

Use properties instead of public fields. Keep the code within properties short, put long code in its own method.

4.9 Enumerations

Use enumerations as much as possible, since they are much easier to understand than numbers, but they have the speed advantage of numbers over strings. Make sure that enumerations match with the corresponding tables in the database (Undefined: 0, English: 1, German: 2). The code has to guarantee (check) that no illegal values can be read from the database or the user.

4.9.1 Special Enumeration Identifiers

 

Identifier

Preferred Values

Description

Use

Min

0

Smallest allowed value

Value range test, loops

Undefined

0

Value not yet initialized (.Net writes 0 into not initialized variables)

Replacement for null

Default

1

Use if value is not defined

Initialisation

Max

 

Highest allowed value

Value range test, loops

 

Example:

 

/// <summary>

/// Enumeration of available languages.

/// </summary>

public enum Language{

牋牋?Min = 0,

牋牋?Undefined = Min,?// for completeness sake, try to avoid use Undefined

牋牋?English,牋牋牋牋?

牋牋?Default = English,

牋牋?German,

牋牋?Max = German

}

 

4.10 Localisation / Globalisation

???

4.10.1 Resource Files

4.11 Exception Handling

The exception handling infrastructure from .NET allows writing code concentrating on the normal program flow, avoiding many if statements handling unexpected error cases. A low level procedure throws an exception if a problem is detected. .NET then unwinds the stack one calling procedure at a time until it finds one that handles that exception.

A good overview over Exception Handling can be found at:

• ?i>MSDN Library, .NET Development, Building Distributed Applications, Architectural Topics, Exception Management in .NET.

MSDN Library, .NET Development, .NET Framework, Programming with the .NET Framework, Handling and Throwing Exceptions, Best Practices for Handling Exceptions

4.11.1 Custom Exceptions

.NET organizes exceptions hierarchically through class inheritance. All custom exceptions have to be derived from ApplicationException. Their name should start with PBox to distinct them from other exceptions and to avoid name collision.

 

Overview over part of exception hierarchy:

 

Exception

+-----SystemException

|牋牋 +-----IndexOutOfRangeException

|牋牋 +-----NullReferenceException

|牋牋 +-----InvalidOperationException

|牋牋 +-----ArgumentException

|牋牋 |牋牋 +-----ArgumentNullException

|牋牋 |牋牋 +-----ArgumentOutOfRangeException

|牋牋 +-----DataException? for errors detected by ADO.NET, like datasets, etc.

|牋牋 | 牋牋+-----InvalidConstraintException

|牋牋 |牋牋 +-----MissingPrimaryKeyException

|牋牋 |牋牋 +-----NoNullAllowedException

|牋牋 |牋牋 +-----ReadOnlyException

|牋牋 |牋牋 +-----RowNotInTableException

|牋牋 +-----IOException

|牋牋 |牋牋 +-----DirectoryNotFoundException

|牋牋 |牋牋 +-----FileNotFoundException

|牋牋 +-----NullReferenceException

|牋牋 +-----SQLException牋 for errors detected by SQL server

+-----ApplicationException牋

|牋牋 +-----PeterBoxException牋 for PeterBox specific exceptions

|牋牋 |牋牋 +----- PBoxDataBaseException

|牋牋 |牋牋牋牋牋 +-----PBoxNoDBAccessException牋 databse cannot be reached

|牋牋 |牋牋 牋牋牋+-----PBoxSPProblemException牋 Stored Procedure cannot be run

 

A custom exception needs at least three constructors. Give the default constructor a default error message:

 

牋牋?/// <summary>

牋牋?/// No acces to database exception

牋牋?/// </summary>

牋牋?public class PBoxNoDBAccessException: PBoxDataBaseException{

 

牋牋牋牋牋?/// <summary> Default constructor </summary>

牋牋牋牋牋?public PBoxNoDBAccessException(): base("No access to datbase") {

牋牋牋牋牋?}

牋牋?

牋牋牋牋牋?/// <summary> Constructor accepting a single string message</summary>

牋牋牋牋牋?/// <param name="message"></param>

牋牋牋牋牋?public PBoxNoDBAccessException(string message): base(message) {

牋牋牋牋牋?}

 

牋牋牋牋牋?/// <summary>

牋牋牋牋牋?/// Constructor accepting a string message and an inner exception

牋牋牋牋牋?/// which will be wrapped by this custom exception class

牋牋牋牋牋?/// </summary>

牋牋牋牋牋?/// <param name="message"></param>

牋牋牋牋牋?/// <param name="innerException"></param>

牋牋牋牋牋?public PBoxNoDBAccessException(string message,

牋牋牋牋牋牋牋牋?Exception innerException): base(message, innerException) {

牋牋牋牋牋?}

牋牋?}

 

4.11.2 Problem Detection and Exception Throwing

Any line of code can fail. It is the duty of the programmer to make sure that any failure is detected, either automatically by the system throwing an exception or programmed explicitly by throwing a custom exception. Depending on the problem detected (for example missing parameter), it might make sense to throw a system exception (ArgumentNullException) instead of a custom exception.

The custom exception class should be defined in the same file where it is thrown. If the custom exception class has its own properties, remember to update the PeterBoxException.ToString method in DBHandler accordingly.

Each exception must generate an error message containing an explanation what was expected, what happened instead and keys helping finding involved data records.

Example ???

Remember, it is bad programming style to used exceptions to handle situations occurring quite often. Exception handling has quite some overhead and should be used for exceptions, not regular cases.

4.11.3 Exception Detection and Handling

• Cleanup of resources

• Tracing of errors

• Debug / release code handling

• Display of unhandled exceptions

4.12 Multithreading

Many simple projects use just one thread. But when a project gets more complicate, often more than one thread is used, e.g. to respond quickly to user request even if heavy computation is performed. Multitasking programming is tricky, because errors can easily be produced which can hardly be found by testing and occur only sporadically. There are 2 kind of errors that occur:

1) Two threads try to access the same object. If one thread is reading an object which is changed the same time by another thread, the value read might be corrupted. To protect against this, locking is used, i.e. the one who wants to write locks the object and delays the reading until writing is finished. It抯 easily to forget locking and all tests will run fine. But once in a while data gets corrupted and it抯 extremely difficult to find the reason !

The .Net library protects all static methods and properties against multithreading problems, but not non static members, because locking can cause severe performance and other problems. However, objects are often accessed by only one single thread, in which case no locking is needed. For the case that an object is accessed by several threads, .Net provides locking mechanisms for many of its classes.

2) Locking, the solution for protection against concurrent access of multiple threads, introduces 2 new problems of its own:

2a) Deadlock: Thread 1 locks first object A, then object B. Another Thread 2 locks first object B, then object A. Usually this will run just fine (no problems during testing), but it might happen that both threats run at the same time and block each other indefinitely. Thread 1 can lock object A, but waits forever to get a lock for object B.

Deadlocks can usually be avoided by locking objects in the same sequence, i.e. task 1 and task 2 lock first object B.

2b) Race Condition: Normally it抯 not advisable to hold a lock for a long time (e.g. to access a file), because the other thread might be able to do some meaningfull work in the meantime, after all that抯 the reason why multithreading. However, if thread locks and reads object A, releases the lock, does some work and then locks object A again for writing, object A might be changed by thread 2 in the meantime and object A gets corrupted by the write of task 1. Be aware that there are a great variety of race conditions !

4.12.1 Recommendations

• Use multithreading wisely (sparingly)

• Avoids multithreading problems by using queues, etc.

• Limit the amount of information modified by several threats as much as possible. Often it is better to use a flag protecting the access to an object than locking the object and its processing.

• If possible, use Interlocked instead of Montor or lock lock statement. Interlock has very little overhead. It allows to change an integer value without getting interrupted. This is usually all functionally needed if one wants to implement locking instead of using library provided solutions.

4.13 Remoting

???

4.14 Debugging

???

4.15 Tracing

Good code is written in such a way that the system keeps running if an error occurs (i.e. try, catch statements). Even better code writes information about the problem encountered (i.e. what it was and where exactly it occurred) to a trace (error log). The user might hardly notice that there was a problem, but the developer gets automatically all information needed to analise the problem.

A System should react differently whether it is being debugged or operational. If a debugger is used, execution should stop where the problem occurred (breakpoint). If it抯 tested without a debugger running, all problems should be written to a file (trace, error log). If the system is operational, most problems will be traced too, however the developer might decide not to trace some minor problems.

.NET provides generic tools for tracing, but they don抰 fulfill the above requirements (breakpoint when in debugger, tracing when not in debugger). This chapter describes a better solution.

For an additional tracing tool provided by ASP.NET see chapter 6.9 ASP.NET Tracing.

4.15.1 PBoxTracing

4.15.2 Different behavior debug / relase build

4.15.3 PBoxTracing Output Media

Per default, a tracing information is written to the output window when the debugger is running

Screen

File

EventLog

Special tracing classes are used for Testing (see ???) and ASP.NET (see ???).

Debug only code

 

[Conditional("DEBUG")]

牋牋牋牋牋?public void MyDebugMethod ()

牋牋牋牋牋?{

牋牋牋牋牋牋牋牋? // debugging code here...

牋牋牋牋牋?}牋

 

Additional debug through configuration change:

 

牋牋牋? <configuration>牋牋牋?

牋牋牋牋牋牋牋??

牋牋牋牋牋? <system.diagnostics>

牋牋牋牋牋牋牋牋牋牋牋 <switches>

牋牋牋牋牋牋牋牋? <add name="DebugSwitch" value="1" />

牋牋牋牋牋牋牋? </switches>

牋牋牋牋牋? </system.diagnostics>

牋牋牋牋 </configuration>

 

It is a good idea to make these switches static so that they are read in only once. After they are declared debugging code can be written as shown below?

 

牋牋?public static BooleanSwitch debug = new BooleanSwitch("DebugSwitch", "Debug Switch");?

 

牋牋牋牋牋? if(debug.Enabled)

牋牋牋牋牋?{

牋 牋牋牋牋牋牋牋?/ debugging action here...

牋牋牋牋牋?}?

 

???

4.16 Configuration

Use the debug and release configuration to differentiate between development and production code. No code change should be needed to switch from debug to release code.

4.17 Peer Review .NET Code

• Is the code well commented and are the comments up to date ?

• Is for every constant number a constant or enumeration defined ?

• Are all variables defined at the proper place (as local as possible) and with the correct modifiers ?

• Proper detection of error conditions ?

- Is code written to detect problems ?

- Are exceptions caught as needed ?

- Are problems reported to log or user ?

- If an exception is handled locally, does the code properly rethrow exceptions for all not handled exceptions ?

• Nested if statements:

- Are error conditions detected and handled first ?

- Has every if statement an else clause ?

• Are conditional and &&, respective or || used instead of &, | ?

• Is every property, non test method, etc. covered by a test method ?

• For objects accessed by multiple threads:

- Is proper locking used ?

- Are deadlocks prevented ?

- Are race conditions prevented ?

• Is code written in the right layer (separation of user interface / middle layer / database access /?general library)

 

???

Chapters Overview

1Introduction
2Overview Development Process
3General Guidelines
4.NET Framework
5Windows Client
6ASP.NET
7HTML
8Cascading Style Sheets
9ADO.NET
10SQL Server
11Appendix
  Version 0.8b contact@peterbox.com ©2002 Peter Huber