.Net Framework Design Guidelines

Common Design Patterns

ConsiderProviding aggregate components for commonly used feature areas.
DoModel high-level concepts (physical objects) rather than system-level tasks with aggregate components.
DoIncrease visibility of aggregate components by giving them names that correspond to well-known entities of the system, such as MessageQueue, Process, or EventLog.
DoDesign aggregate components so they can be used after very simple initialization.
Do NotRequire the users of aggregate components to explicitly instantiate multiple objects in a single scenario.
DoMake sure aggregate components support the Create-Set-Call usage pattern, where developers expect to be able to implement most scenarios by instantiating the component, setting its properties, and calling simple methods.
DoProvide a default or a very simple constructor for all aggregate components.
DoProvide properties with getters and setters corresponding to all parameters of aggregate component constructors.
DoUse events instead of delegate-based APIs in aggregate components.
ConsiderUsing events instead of virtual members that need to be overridden.
Do NotRequire users of aggregate components to inherit, override methods, or implement any interfaces in common scenarios.
Do NotRequire users of aggregate components to do anything besides writing code in common scenarios.
ConsiderMaking changes to aggregate components' modes automatic.
Do NotDesign factored types that have modes.
ConsiderIntegrating your aggregate components with Visual Studio Designers.
ConsiderSeparating aggregate components and factored types into different assemblies.
ConsiderExposing access to internal factored types of an aggregate component.
DoUse the following convention for defining APIs for asynchronous operations: 1. public <return> Operation(<parameters>, <out params>) 2. public IAsyncResult BeginOperation(<parameters>, AsyncCallback callback, object state) 3. public <return> EndOperation(IAsyncResult asyncResult, <out params>)
DoEnsure that the return type of the Begin method implements IAsyncResult.
DoEnsure that any by-value and ref parameters of the synchronous method are represented as by-value parameters of the Begin method.
DoEnsure that the return type of the End method is the same as the return type of the synchronous method.
DoEnsure that any out and ref parameters of the synchronous method are represented as out parameters of the End method.
Do NotContinue the asynchronous operation if the Begin method throws an exception.
DoNotify the caller that the asynchronous operation completed via all of the following mechanisms in this order: 1. Set IAsyncResult.IsCompleted to true. 2. Call the async callback.
DoThrow from the End method to indicate that the asynchronous operation could not complete successfully.
DoComplete all remaining work synchronously once the End method is called.
ConsiderThrowing an InvalidOperationException if the End method is called with the same IAsyncResult twice, or if the IAsyncResult was returned from an unrelated Begin method.
DoSet IAsyncResult.CompletedSynchronously to true if and only if the async callback will be run on the thread that called Begin.
DoImplement the Basic Dispose Pattern on types containing instances of disposable types.
DoImplement the Basic Dispose Pattern and provide a finalizer on types holding resources that need to be freed explicitly and that do not have finalizers.
ConsiderImplementing the Basic Dispose Pattern on classes that themselves don't hold unmanaged resources or disposable objects but are likely to have subtypes that do.
DoDeclare a protected virtual void Dispose(bool disposing) method to centralize all logic related to releasing unmanaged resources.
DoImplement the IDisposable interface by simply calling Dispose(true) followed by GC.SupressFinalize(this).
Do NotMake the parameterless Dispose method virtual.
Do NotDeclare any overloads of the Dispose method other than Dispose() and Dispose(bool).
DoAllow the Dispose(bool) method to be called more than once.
AvoidThrowing an exception from within Dispose(bool) except under critical situations where the containing process has been corrupted (leaks, inconsistent shared state, etc.).
DoThrow an ObjectDisposedException from any member that can not be used after the object has been disposed.
ConsiderProviding method Close(), in addition to the Dispose(), if close is standard terminology in the area.
AvoidMaking types finalizable.
Do NotMake value types finalizable.
DoMake a type finalizable, if the type is responsible for releasing an unmanaged resource that does not have its own finalizer
DoImplement the Basic Dispose Pattern on every finalizable type.
Do NotAccess any finalizable objects in the finalizer code path, as there is significant risk that they will have already been finalized.
DoMake your Finalize method protected.
Do NotLet exceptions escape from the finalizer logic, except for system-critical failures.
ConsiderCreating and using a critical finalizable object (a type with a hierarchy that contains CriticalFinalizerObject) for situations in which a finalizer absolutely must execute even in the face of forced application domain unloads and thread aborts.
DoPrefer constructors to factories, as they are generally more usable, consistent, and convenient than specialized construction mechanisms.
ConsiderUsing a factory if you need more control than can be provided by constructors of the creation of the instances.
DoUse a factory in cases where a developer might not know which type to construct, such as wen coding against a base type or interface.
ConsiderUsing a factory if having a named method is the only way to make the operation self-explanatory.
DoUse a factory for conversion-style operations.
DoPrefer implementing factory operations as methods, rather than properties.
DoReturn created instances as method return values, not as out parameters.
ConsiderNaming factory methods by concatenating Create and the name of the type being created.
ConsiderNaming factory types by concatenating the name of the type being created and Factory.
ConsiderUsing the Optional Feature Pattern for optional features in abstractions.
DoProvide a simple Boolean property that clients can use to determine whether an optional feature is supported.
DoUse virtual methods on the base class that throw NotSupportedException to define optional features.
AvoidMaking public members virtual.
ConsiderThe Template Method Pattern to provide more controlled extensibility.
ConsiderNaming protected virtual members that provide extensibility points for nonvirtual members by suffixing the nonvirtual member name with “Core”.
DoPrefer method parameters as the mechanism for users to provide timeout time.
DoPrefer using TimeSpan to represent timeout time.
DoThrow System.TimeoutException when a timeout elapses.
Do NotReturn error codes to indicate timeout expiration.