Friday, November 14, 2008



Sunday, November 2, 2008

Error Raising & Handling

The following rules outline the guidelines for raising and handling errors:
1. All code paths that result in an exception should provide a method to check for success without throwing an exception. For example, to avoid a FileNotFoundException you can call File.Exists. This might not always be possible, but the goal is that under normal execution no exceptions should be thrown.
2. Exceptions classes should always be serializable.
3. End Exception class names with the Exception suffix as in the following code example.
Visual Basic
_ Public Class FileNotFoundException
Inherits Exception
' Implementation code goes here.
End Class
C# [Serializable()]
public class FileNotFoundException : Exception
{
// Implementation code goes here.
}
4. Use the three common constructors shown in the following code example when creating exception classes in C# and the Managed Extensions for C++.
Visual Basic
Public Class XXXException
Inherits ApplicationException
Public Sub New()
' Implementation code goes here.
End Sub
Public Sub New(message As String)
My.Base.New(message)
End Sub
Public Sub New(message As String, inner As Exception)
My.Base.New(message, inner)
End Sub
End Class
C#
public class XXXException : ApplicationException
{
XxxException() {... }
XxxException(string message) {... }
XxxException(string message, Exception inner) {... }
}
5. In most cases, use the predefined exception types. Only define new exception types for programmatic scenarios, where you expect users of your class library to catch exceptions of this new type and perform a programmatic action based on the exception type itself. This is
in lieu of parsing the exception string, which would negatively impact performance and maintenance. For example, it makes sense to define a FileNotFoundException because the developer might decide to create the missing file. However, a FileIOException is not something that would typically be handled specifically in code.
6. Do not derive new exceptions directly from the base class Exception. Instead, derive from ApplicationException.
7. Group new exceptions derived from the base class Exception by namespace. For example, there will be derived classes for XML, IO, Collections, and so on. Each of these areas will have their own derived classes of exceptions as appropriate. Any exceptions that other library or application writers want to add will extend the Exception class directly. You should create a single name for all related exceptions, and extend all exceptions related to that application or library from that group.
8. Use a localized description string in every exception. When the user sees an error message, it will be derived from the description string of the exception that was thrown, and never from the exception class.
9. Create grammatically correct error messages with punctuation. Each sentence in the description string of an exception should end in a period. Code that generically displays an exception message to the user does not have to handle the case where a developer forgot the final period.
10. Provide exception properties for programmatic access. Include extra information (other than the description string) in an exception only when there is a programmatic scenario where that additional information is useful. You should rarely need to include additional information in an exception.
11. Do not use exceptions for normal or expected errors, or for normal flow of control. (Throwing an exception is an extremely costly operation.)
12. You should return null for extremely common error cases. For example, a File.Open command returns a null reference if the file is not found, but throws an exception if the file is locked.
13. Design classes so that in the normal course of use an exception will never be thrown. In the following code example, a FileStream class exposes another way of determining if the end of the file has been reached to avoid the exception that will be thrown if the developer reads past the end of the file.
Visual Basic
Class FileRead
Sub Open()
Dim stream As FileStream = _
File.Open("myfile.txt",FileMode.Open)
Dim b As Byte
' ReadByte returns -1 at end of file.
While b = stream.ReadByte() <> true
' Do something.
End While
End Sub
End Class

C#
class FileRead
{
void Open()
{
FileStream stream =
File.Open("myfile.txt", FileMode.Open);
byte b;
// ReadByte returns -1 at end of file.
while ((b = stream.ReadByte()) != true)
{
// Do something.
}
}
}
14. Throw the InvalidOperationException exception if a call to a property set accessor or method is not appropriate given the object's current state.
15. Throw an ArgumentException or create an exception derived from this class if invalid parameters are passed or detected.
16. Be aware that the stack trace starts at the point where an exception is thrown, not where it is created with the new operator. Consider this when deciding where to throw an exception.
17. Use the exception builder methods. It is common for a class to throw the same exception from different places in its implementation. To avoid repetitive code, use helper methods that create the exception using the new operator and return it. The following code example shows how to implement a helper method.
Visual Basic
Class File
Private mFileName As String
Public Function Read(bytes As Int32) As Byte()
If Not ReadFile(handle, bytes) Then
Throw New FileIOException()
End If
End Function
Private Function NewFileIOException() As FileException
Dim Descroption As String = _
' Build localized string, include fileName.
Return New FileException(Descroption)
End Sub
End Class
C#
class File
{
string mFileName;
public byte[] Read(int bytes)
{
if (!ReadFile(handle, bytes))
throw NewFileIOException();
}
FileException NewFileIOException()
{
string description =
// Build localized string, include fileName.
return new FileException(description);
}
}
18. Throw exceptions instead of returning an error code or HRESULT.
19. Throw the most specific exception possible.
20. Create meaningful message text for exceptions, targeted at the developer.
21. Set all fields on the exception you use.
22. Use Inner exceptions (chained exceptions). However, do not catch and re-throw exceptions unless you are adding additional information and/or changing the type of the exception.
23. Do not create methods that throw NullReferenceException or IndexOutOfRangeException. (These are hard debug when the application is in a production environment as they do not give enough information about the runtime situation.)
24. Perform argument checking on protected (Family) and internal (Assembly) members. Clearly state in the documentation if the protected method does not do argument checking. Unless otherwise stated, assume that argument checking is performed. There might, however, be performance gains in not performing argument checking.
25. Clean up any side effects when throwing an exception. Callers should be able to assume that there are no side effects when an exception is thrown from a function. For example, if a Hashtable.Insert method throws an exception, the caller can assume that the specified item was not added to the Hashtable.
26. Very rarely “absorb” exceptions by having a try…catch block that does nothing.
27. Consider using try…finally without a catch block as a “last chance” option to restore state. This is a powerful method of ensuring that locks are unlocked or that state is returned to it’s original values when an error occurs.
28. Consider that if you overload ToString on an exception class, ASP.NET does not use ToString to present the exception to the developer, hence any additional state information you dump in the ToString method will not be shown.
Standard Exception Types
The following table lists the standard exceptions provided by the runtime and the conditions for which you should create a derived class.

Exception type Base type Description Example

Exception Object Base class for None (use a derived class of this-
all exceptions. Exception)

SystemException Exception Base class for all runtime None
generated errors.

Nested Type Usage

A nested type is a type defined within the scope of another type. Nested types are very useful for encapsulating implementation details of a type, such as an enumerator over a collection, because they can have access to private state.
Public nested types should be used rarely. Use them only in situations where both of the following are true:
1. The nested type logically belongs to the containing type.
2. The nested type is not used often, or at least not directly.
The following examples illustrates how to define types with and without nested types:
Visual Basic
' With nested types.
ListBox.SelectedObjectCollection
' Without nested types.
ListBoxSelectedObjectCollection
' With nested types.
RichTextBox.ScrollBars
' Without nested types.
RichTextBoxScrollBars
C#
// With nested types.
ListBox.SelectedObjectCollection
// Without nested types.
ListBoxSelectedObjectCollection
// With nested types.
RichTextBox.ScrollBars
// Without nested types.
RichTextBoxScrollBars
Do not use nested types if the following are true:
1. The type is used in many different methods in different classes. The FileMode Enumeration is a good example of this kind of type.
2. The type is commonly used in different APIs. The StringCollection class is a good example of this kind of type.

Attribute Usage

The .NET Framework enables developers to invent new kinds of declarative information, to specify declarative information for various program entities, and to retrieve attribute information in a run-time environment. For example, a framework might define a HelpAttribute attribute that can be placed on program elements such as classes and methods to provide a mapping from program elements to their documentation. New kinds of declarative information are defined through the declaration of attribute classes, which might have positional and named parameters. For more information about attributes, see Writing Custom Attributes.
The following rules outline the usage guidelines for attribute classes:
1. Add the Attribute suffix to custom attribute classes, as shown in the following example.
Visual Basic
AllowMultiple := True)> _
Public Class ObsoleteAttribute
Inherits Attribute
' Insert code here.
End Class
C#
[AttributeUsage(AttributeTargets.All, Inherited = false,
AllowMultiple = true)]
public class ObsoleteAttribute: Attribute {}
2. Seal attribute classes whenever possible, so that classes cannot be derived from them. (This improves performance.)
3. Use positional arguments for required parameters.
4. Use named arguments for optional parameters.
5. Do not name a parameter with both named and positional arguments.
6. Provide a read-only property with the same name as each positional argument, but change the case to differentiate between them.
7. Provide a read/write property with the same name as each named argument, but change the case to differentiate between them.
Visual Basic
Public Class NameAttribute
Inherits Attribute
Public Sub New(username As String)
' Implement code here.
End Sub
Public ReadOnly Property UserName() As String
Get
Return UserName
End Get
End Property
Public Property Age() As Int32
Get
Return Age
End Get
Set (value As Int32)
Age = value
End Set
End Property
' Positional argument.
End Class
C#
public class NameAttribute: Attribute
{
public NameAttribute (string username)
{
// Implement code here.
}
public string UserName
{
get
{
return UserName;
}
}
public int Age
{
get
{
return Age;
}
set
{
Age = value;
}
}
// Positional argument.
}

Enum Usage Guidelines

The following rules outline the usage guidelines for enumerations:
1. Use an enum to strongly type parameters, properties, and return types. Always define enumerated values using an enum if they are used in a parameter or property. This allows development tools to know the possible values for a property or parameter. The following example shows how to define an enum type.
Visual Basic
Public Enum FileMode
Append
Create
CreateNew
Open
OpenOrCreate
Truncate
End Enum
C#
public enum FileMode
{
Append,
Create,
CreateNew,
Open,
OpenOrCreate,
Truncate
}

The following example shows the constructor for a FileStream object that uses the FileMode enum.
Visual Basic
Public Sub New(ByVal path As String, _
ByVal mode As FileMode)
C#
public FileStream(string path, FileMode mode);
2. Use the System.FlagsAttribute class to create custom attribute for an enum if a bitwise OR operation is to be performed on the numeric values. This attribute is applied in the following code example.
Visual Basic
_
Public Enum Bindings
IgnoreCase = &H1
NonPublic = &H2
Static = &H4
InvokeMethod = &H100
CreateInstance = &H200
GetField = &H400
SetField = &H800
GetProperty = &H1000
SetProperty = &H2000
DefaultBinding = &H10000
DefaultChangeType = &H20000
[Default] = DefaultBinding Or DefaultChangeType
ExactBinding = &H40000
ExactChangeType = &H80000
BinderBinding = &H100000
BinderChangeType = &H200000
End Enum
C#
[Flags]
public enum Bindings
{
IgnoreCase = 0x01,
NonPublic = 0x02,
Static = 0x04,
InvokeMethod = 0x0100,
CreateInstance = 0x0200,
GetField = 0x0400,
SetField = 0x0800,
GetProperty = 0x1000,
SetProperty = 0x2000,
DefaultBinding = 0x010000,
DefaultChangeType = 0x020000,
Default = DefaultBinding | DefaultChangeType,
ExactBinding = 0x040000,
ExactChangeType = 0x080000,
BinderBinding = 0x100000,
BinderChangeType = 0x200000
}

Note An exception to this rule is when encapsulating a Win32 API. It is common to have internal definitions that come from a Win32 header. You can leave these with the Win32 casing, which is usually all capital letters.
3. Use an enum with the flags attribute only if the value can be completely expressed as a set of bit flags. Do not use an enum for open sets (such as the operating system version).
4. Do not assume that enum arguments will be in the defined range. Perform argument validation as illustrated in the following code example.
Visual Basic
Public Sub SetColor(newColor As Color)
If Not [Enum].IsDefined(GetType(Color), newColor) Then
Throw New ArgumentOutOfRangeException()
End If
End Sub
C#
public void SetColor (Color color)
{
if (!Enum.IsDefined (typeof(Color), color)
throw new ArgumentOutOfRangeException();
}
5. Use an enum instead of static final constants.
6. Use type Int32 as the underlying type of an enum unless either of the following is true:
• The enum represents flags and there are currently more than 32 flags, or the enum might grow to many flags in the future.
• The type needs to be different from int for backward compatibility.
7. Do not use a non-integral enum type. Use only Byte, Int16, Int32, or Int64.
8. Do not use an Enum suffix on enum types.

Structure Usage Guidelines

It is recommended that you use a structure for types that meet any of the following criteria:
1. Act like primitive types.
2. Have an instance size under 16 bytes.
3. Are immutable.
4. Value semantics are desirable.
The following example shows a correctly defined structure.
Visual Basic
Public Structure Int32
Implements IFormattable
Implements IComparable
Public Const MinValue As Integer = -2147483648
Public Const MaxValue As Integer = 2147483647
Private intValue As Integer
Overloads Public Shared Function ToString(i As Integer) As String
' Insert code here.
End Function
Overloads Public Function ToString(ByVal format As String, _
ByVal formatProvider As IFormatProvider) _
As String Implements
IFormattable.ToString
' Insert code here.
End Function
Overloads Public Overrides Function ToString() As String
' Insert code here.
End Function
Public Shared Function Parse(s As String) As Integer
' Insert code here.
Return 0
End Function
Public Overrides Function GetHashCode() As Integer
' Insert code here.
Return 0
End Function
Public Overrides Overloads Function Equals(obj As Object) _
As Boolean
' Insert code here.
Return False
End Function
Public Function CompareTo(obj As Object) As Integer _
Implements IComparable.CompareTo
' Insert code here.
Return 0
End Function
End Structure
C#
public struct Int32: IComparable, IFormattable
{
public const int MinValue = -2147483648;
public const int MaxValue = 2147483647;
public static string ToString(int i)
{
// Insert code here.
}
public string ToString(string format,
IFormatProvider formatProvider)
{
// Insert code here.
}
public override string ToString()
{
// Insert code here.
}
public static int Parse(string s)
{
// Insert code here.
return 0;
}
public override int GetHashCode()
{
// Insert code here.
return 0;
}
public override bool Equals(object obj)
{
// Insert code here.
return false;
}
public int CompareTo(object obj)
{
// Insert code here.
return 0;
}
}
When using a structure, do not provide a default constructor. The runtime will insert a constructor that initializes all the values to a zero state. This allows arrays of structures to be created more efficiently. You should also allow a state where all instance data is set to zero, false, or null (as appropriate) to be valid without running the constructor

Operator Overloading

1. Define operators on value types that are logical built-in language types, such as the System.Decimal structure.
2. Provide operator-overloading methods only in the class in which the methods are defined.
3. Use the names and signature conventions described in the Common Language Specification (CLS).
4. Use operator overloading in cases where it is immediately obvious what the result of the operation will be. For example, it makes sense to be able to subtract one Time value from another Time value and get a TimeSpan. However, it is not appropriate to use the or operator to create the union of two database queries, or to use shift to write to a stream.
5. Overload operators in a symmetric manner. For example, if you overload the equality operator (==), you should also overload the not equal operator (!=).
6. Provide alternate signatures. Most languages do not support operator overloading. For this reason, always include a secondary method with an appropriate domain-specific name that has the equivalent functionality. It is a Common Language Specification (CLS) requirement to provide this secondary method. The following example is CLS-compliant



C#
class Time
{
TimeSpan operator -(Time t1, Time t2) { }
TimeSpan Difference(Time t1, Time t2) { }
}
The following table contains a list of operator symbols and the corresponding alternative methods and operator names.

Array Usage

Arrays vs. Collections
Class library designers might need to make difficult decisions about when to use an array and when to return a collection. Although these types have similar usage models, they have different performance characteristics. You should use a collection in the following situations:
1. When Add, Remove, or other methods for manipulating the collection are supported.
2. To add read-only wrappers around internal arrays.


Using Indexed Properties in Collections
You should use an indexed property only as a default member of a collection class or interface. Do not create families of functions in non-collection types. A pattern of methods, such as Add, Item, and Count, signal that the type should be a collection.



Array Valued Properties
You should use collections to avoid code inefficiencies. In the following code example, each call to the myObj property creates a copy of the array. As a result, 2n+1 copies of the array will be created in the following loop.
Visual Basic
Dim i As Int32
For i = 0 To obj.myObj.Count - 1
DoSomething(obj.myObj(i))
Next i
C#
for (int i = 0; i < obj.myObj.Count; i++)
DoSomething(obj.myObj[i]);


Returning Empty Arrays
String and Array properties should never return a null reference. Null can be difficult to understand in this context. For example, a user might assume that the following code will work.
Visual Basic
Public Sub DoSomething()
Dim s As String = SomeOtherFunc()
If s.Length > 0 Then
' Do something else.
End If
End Sub
C#
public void DoSomething()
{
string s = SomeOtherFunc();
if (s.Length > 0)
{
// Do something else.
}
}
The general rule is that null, empty string (""), and empty (0 item) arrays should be treated the same way. Return an empty array instead of a null reference.