Framework Guidelines

Edit this page

Use C# type aliases instead of the types from the System namespace (RG2201)

For instance, use object instead of Object, string instead of String, and int instead of Int32. These aliases have been introduced to make the primitive types first class citizens of the C# language, so use them accordingly.

Exception: When referring to static members of those types, it is custom to use the full CLS name, e.g. Int32.Parse() instead of int.Parse(). The same applies to members that need to specify the type they return, e.g. ReadInt32, GetUInt16.

Prefer language syntax over explicit calls to underlying implementations (RG2202)

Language syntax makes code more concise. The abstractions make later refactorings easier (and sometimes allow for extra optimizations).


(string, int) tuple = ("", 1);

rather than:

ValueTuple<string, int> tuple = new ValueTuple<string, int>("", 1);


DateTime? startDate;

rather than:

Nullable<DateTime> startDate;


if (startDate != null) ...

rather than:

if (startDate.HasValue) ...


if (startDate > DateTime.Now) ...

rather than:

if (startDate.HasValue && startDate.Value > DateTime.Now) ...


(DateTime startTime, TimeSpan duration) tuple1 = GetTimeRange();
(DateTime startTime, TimeSpan duration) tuple2 = GetTimeRange();

if (tuple1 == tuple2) ...

rather than:

if (tuple1.startTime == tuple2.startTime && tuple1.duration == tuple2.duration) ...

Choose appropriate .NET exceptions to throw (RG2205)

  • Extension methods should throw ArgumentNullExceptions when their this parameter is null, instead of throwing NullReferenceException:

Don’t hard-code strings that change based on the deployment (RG2207)

Examples include connection strings, server addresses, etc. Use Resources, the ConnectionStrings property of the ConfigurationManager class, or the Settings class generated by Visual Studio. Maintain the actual values into the app.config or web.config (and most definitely not in a custom configuration store).

Build with the highest warning level (RG2210)

Configure the development environment to use Warning Level 4 for the C# compiler, and enable the option Treat warnings as errors . This allows the compiler to enforce the highest possible code quality.

Avoid LINQ for simple expressions (RG2220)

Rather than:

var query = from item in items where item.Length > 0 select item;

prefer the use of extension methods from the System.Linq namespace:

var query = items.Where(item => item.Length > 0);

Since LINQ queries should be written out over multiple lines for readability, the second example is a bit more compact.

Use lambda expressions instead of anonymous methods (RG2221)

Lambda expressions provide a more elegant alternative for anonymous methods. So instead of:

Customer customer = Array.Find(customers, delegate(Customer customer)
	return customer.Name == "Tom";

use a lambda expression:

Customer customer = Array.Find(customers, customer => customer.Name == "Tom");

Or even better:

var customer = customers.FirstOrDefault(customer => customer.Name == "Tom");

Only use the dynamic keyword when talking to a dynamic object (RG2230)

The dynamic keyword has been introduced for working with dynamic languages. Using it introduces a serious performance bottleneck because the compiler has to generate some complex Reflection code.

Use it only for calling methods or members of a dynamically created instance class (using the Activator) as an alternative to Type.GetProperty() and Type.GetMethod(), or for working with COM Interop types.

Favor async/await over Task continuations (RG2235)

Using the new C# 5.0 keywords results in code that can still be read sequentially and also improves maintainability a lot, even if you need to chain multiple asynchronous operations. For example, rather than defining your method like this:

public Task<Data> GetDataAsync()
  return MyWebService.FetchDataAsync()
    .ContinueWith(t => new Data(t.Result));

define it like this:

public async Task<Data> GetDataAsync()
  string result = await MyWebService.FetchDataAsync();
  return new Data(result);

Tip: Even if you need to target .NET Framework 4.0 you can use the async and await keywords. Simply install the Async Targeting Pack.

A class should own managed or unmanaged resources, but not both (RG2240)

For the purposes of this guideline “unmanaged resources” means anything that needs to be cleaned up by the process, but which won’t be handled by the garbage collector. Examples include temporary files, temporary databases, windows handles, memory allocated by native code, etc.

Wrap unmanaged resources to turn them into managed resources:

  • Create a sealed class with a single responsibility of managing the resource
  • Implement IDisposable and clean up the resource in the Dispose() method
  • Write a finalizer for the class which calls Dispose()
    • Remember that the finalizer will run even if the constructor throws an exception, and needs to handle that case (although exceptions should be unlikely because of RG1002)

For other classes which own managed resources:

  • Implement IDisposable and call IDisposable.Dispose() on all owned disposable objects.

Following this guideline should mean you can avoid the more complicated ‘dispose pattern’ when cleaning up resources.

Follow the Microsoft REST guidelines when writing web APIs (RG2250)

Use the Microsoft REST API guidelines as a sensible standard when writing web APIs.

We try to fit our API operations into a resource-based model. In the rare cases where this isn’t possible (for example, if the API endpoint is just doing some pure calculation) we’re allowed to fall back to RPC operations isolated under some /rpc/ namespace.