Posted by: rcosic | 01/04/2009

.NET Framework Design Guidelines – a headstone of Code Analysis

Now that I’ve wrote almost everything to give you an overview of Code Analysis in Visual Studio 2008, the very first thing is missing – a basis for code analysis. You might be wondering how the heck these rules came out. What is the origin of information about different kinds of things you should consider when coding? Based on which best practices and documentation the rules have been created? The answer is simple – based on Framework Design Guidelines book, an ultimate source of the Microsoft’s knowledge on how to set the code right.

fdgThe first edition of this book is available for quite time, but the second edition is more exciting because it tackles .NET Framework 3.5. The book was written by Krzysztof Cwalina (jeez, I tought my name is weird!) and Brad Abrams. You can get more info about the book and how they made it at their blog sites.
What this book brings us? Essentially, it teach us how to design code for the Microsoft .NET Framework. It presents us the general philosophy and fundamental priciples of framework design, naming guidelines, guidelines for the design and extending of types and members of types, extensibility issues and guidelines, exceptions design, common framework design patterns, and lot more.

Code Analysis for managed code analyzes our code based on these Framework Design Guidelines. There are other guidelines which effect other platforms, such as native C/C++, but I will not present it here. The most important thing is that these guidelines are a headstone of building rules for both FxCop and CA in Visual Studio Team Editions.

What we get with applying these design guidelines anyway? First of all – consistency and predictability. Classes generated by this guidance will all have the same look-appeal and we can easily read them and understand. Even another developer would parse them quickly and have shorter time to adopt himself to the work on the project.

Also, there is a real threat that classes can be bug-prone or even dangerous if we do not take care on lots of aspects when building them. I cannot remember all best practices when building my code, and very often I find myself surprised when some warnings pop up that warn me to fix some, at first look, obvious errors. This shortens bug-fixing path, and obviously makes it less expensive, because the bugs are found earlier in development cycle.

I will not go through all the guidelines now, but I’ll rather explain some of them I think are good to know.

Naming Guidelines

These are obvious and easy to explain. Microsoft prescribes that we should stick to certain capitalization styles when naming identifiers : pascal case for naming namespaces, interfaces, classes, methods, properties, events, enumerations (their type and values), exceptions, and read-only static fields; and camel case for naming parameters of methods. Also, we are advised to take an attention of case sensitivity. You should not use two names that differ only by case. Also, you should stick to that case when you use it in code. Usage of abbreviations is not encouraged except if it is the case with a lenghty phrase name, such as, user interface (UI), Windows Communication Foundation (WCF), and so on. When pasting an abbreviation to another words in identifier, a pascal or camel case is advised. When choosing words for type names, you should not pick the reserved words, and also avoid the use of type names (for example, TrueInt32). In naming namespaces, you should stick to the standard notation:     CompanyName.TechnologyName[.Feature][.Design], in which you should be careful of picking names of their references. The usage of the underscore sign (_) is not encouraged. 

Take care of using (or not) proper prefixes and suffixes when creating classes, interfaces, members, and so on. For example, a class should not begin with a letter C (as it has been advised a long time ago). Prefix interface names with the letter I, to indicate that the type is an interface. Add the suffix Attribute when creating custom attribute classes. Don’t uses prefix Enum when building enumerations, and name the values in singular. For events, when you create a custom event handler, end it with words EventHandler. Also, you should create it with two standard parameters: sender (with type object) and e (with EventArgs type or derived). Generally, you should use verbs when naming events (for example: Close, Closing, Activate). Do not use the Before/After naming pattern when building two events which go one after another (as been advised before). And finally, try to have the same names (with underscore sign as distinguisher) for matching property to instance field.

Type and Class Member Usage Guidelines

You should use base classes when grouping objects that share a common set of functionality. You should generally write at least one constructor for a class (or hide its usage by leaving it empty). Base classes are more preferrable option than interfaces except a few situations. Use sealed classes if there are only static methods and properties on a class.

For types that act like primitive types, or should be immutable, have an instance size under 16 bytes, or value semantics are desirable, use struct types without providing its default constructor. To strongly type parameters, properties, and return types, use an enum type with Int32 as the underlying type. Do not use it for an open set of values though (for example, rows in database table). Mark it with FlagsAttribute if bitwise OR comparison can be performed (with powers of two).

To provide declarative way of programming, use attributes. They should be sealed whenever possible, specified with AttributeUsage, with positional arguments (constructor parameters) for required parameters, and named arguments (read/write properties) for optional parameters.

When building a class, you will probably write properties first (at least, I do). You should first decide if you should create a property or a method for performing certain operation. In general, methods represent actions –  properties represent data. You should ensure that the setting of data give the same results whatever order is used. Also, properties should be designed to be stateless, i.e., their setting should not commence any operation that can hang if depend on a state of an object. You should implement ISupportInitialize interface to setting up the state in one logical block (BeginInit and EndInit methods are automatically created). Properties shoud notify users that the state has changed with events named <Property>Changed.

When building methods, use overloading to provide different methods that do semantically the same thing. For exposing a method that takes a variable number of arguments, use params type. You should perform basic checking of parameter values (also in property setters and constructors) and throw ArgumentException in case of error.

When building constructors, hide it (make default private) if class contains only static properties and methods. You should avoid lots of code inside constructor and make it lightweight as much as possible. Use parameters in constructors as shortcuts for setting properties, but ensure consistent ordering and naming pattern for constructor parameters.

Error Raising and Handling Guidelines

You should avoid just throwing exception if there is a way to handle the situation without it (by checking something up front). Custom exceptions should end with Exception suffix, and they should be generally avoided in favor of just using the existing ones. You should populate your custom-made exception with standard constructor overloads, and make sure you localize the strings used for messages and so on. You should throw an ArgumentException if invalid parameters are passed or detected, throw an InvalidOperationException if a call to a property set accessor or method is not appropriate given the object’s current state. Do not catch and re-throw exceptions unless you are adding additional information or changing the type of the exception. And throw the most specific exception possible.

Common Design Patterns

You should implement a Dispose pattern only if you have a need for it. That is, your class uses unmanaged resources (window handles, database connections, and so on) which should be explicitly released before the garbage collector frees the object. To implement this pattern you should merely implement IDisposable interface to a class and fill out Dispose method with the cleanup code. In case of inheritance, a parent class should clean up its resources and roll over its children object and call their Dispose methods (so, not clear the resources of children by itself). And also, the design of Dispose pattern must guarranty that the method can be called multiple times without throwing an exception. A Dispose method should call the SuppressFinalize method for the object it is disposing. If the object is currently on the finalization queue, SuppressFinalize prevents its Finalize method from being called. You can review the pattern by seeng the following code snippet:

public class DisposableResource : IDisposable
{
private Stream _resource;
private bool _disposed;

// The stream passed to the constructor
// must be readable and not null.
public DisposableResource(Stream stream)
{
if (stream == null)
throw new ArgumentNullException(“Stream in null.”);
if (!stream.CanRead)
throw new ArgumentException(“Stream must be readable.”);

_resource = stream;

_disposed = false;
}

// Demonstrates using the resource.
// It must not be already disposed.
public void DoSomethingWithResource()
{
if (_disposed)
throw new ObjectDisposedException(“Resource was disposed.”);

// Show the number of bytes.
int numBytes = (int)_resource.Length;
Console.WriteLine(“Number of bytes: {0}”, numBytes.ToString());
}

public void Dispose()
{
Dispose(true);

// Use SupressFinalize in case a subclass
// of this type implements a finalizer.
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
// If you need thread safety, use a lock around these
// operations, as well as in your methods that use the resource.
if (!_disposed)
{
if (disposing)
{
if (_resource != null)
_resource.Dispose();
Console.WriteLine(“Object disposed.”);
}

// Indicate that the instance has been disposed.
_resource = null;
_disposed = true;
}
}
}

Another common pattern is how to implement Equals method and Equality operator. In designing these, we should follow the following principles: implement also GetHashCode method to keep them synchronized; override the == operator (equality) to make the same thing; override Equals method whenever you implement IComparable interface (and if you can, also <, >, != operators); do not throw exceptions in any of these methods but rather return false. There is also a difference between value and reference types in terms of implementing equality operator. As a general rule of thumb, you shoud override default equality operator for value types (as default will not perform well) and avoid or be careful in implementing it for reference types. 
When dealing with results of Equals method, you should make sure that:

  • x.Equals(x) returns true.
  • x.Equals(y) returns the same value as y.Equals(x).
  • (x.Equals(y) && y.Equals(z)) returns true if and only if x.Equals(z) returns true.
  • successive invocations of x.Equals(y) return the same value as long as the objects referenced by x and y are not modified.
  • x.Equals(null) returns false.

Please review the following code to see how you can do it:

public struct Complex
{
double re, im;
public override bool Equals(Object obj)
{
return obj is Complex && this == (Complex)obj;
}
public override int GetHashCode()
{
return re.GetHashCode() ^ im.GetHashCode();
}
public static bool operator ==(Complex x, Complex y)
{
return x.re == y.re && x.im == y.im;
}
public static bool operator !=(Complex x, Complex y)
{
return !(x == y);
}

Security Issues

You should be aware that writing unsecure code is dangerous, but easy to do. You have an option to decorate your source code declaratively with attributes, and/or write imperative security checks inside the code. You should check if the caller of the code or the code itself has required permissions (security demand) and avoid usage of security overrrides and write security-neutral code. You should request the minimum permission your code must receive to run, and also ensure that your code receives only permissions that should receive. For example:

[assembly:FileIOPermissionAttribute(SecurityAction.RequestMinimum, Write=”C:\\test.tmp”)]
[assembly:PermissionSet(SecurityAction.RequestOptional,Unrestricted=false)]

The data which are operated in memory (cached) should be private or protected, and be aware of reflection, debugging, or serialization, because these processes can access private data. In securing exceptions, you should use user-filtered exceptions and protect sensitive details of exceptions. Also, avoid strange characters (quotes, asterisks, etc.) and use parameters instead of strings to avoid SQL injection. If you remembered from my previous posts, CodeDOM can be used to generate classes on-the-fly, but can be vulnerable to malicious attacks. So, be careful when coding this stuff.

Is this all?

Of course not. There are myriads of other guidelines, for example, how to expose functionality to COM, how to use arrays, operator overloading, casting types, threading and asynchronous programming. If you more interested, please visit the MSDN site and try to find what you need.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: