Consider | Providing aggregate components for commonly used feature areas. |
Do | Model high-level concepts (physical objects) rather than system-level tasks with aggregate components. |
Do | Increase visibility of aggregate components by giving them names that correspond to well-known entities of the system, such as MessageQueue, Process, or EventLog. |
Do | Design aggregate components so they can be used after very simple initialization. |
Do Not | Require the users of aggregate components to explicitly instantiate multiple objects in a single scenario. |
Do | Make 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. |
Do | Provide a default or a very simple constructor for all aggregate components. |
Do | Provide properties with getters and setters corresponding to all parameters of aggregate component constructors. |
Do | Use events instead of delegate-based APIs in aggregate components. |
Consider | Using events instead of virtual members that need to be overridden. |
Do Not | Require users of aggregate components to inherit, override methods, or implement any interfaces in common scenarios. |
Do Not | Require users of aggregate components to do anything besides writing code in common scenarios. |
Consider | Making changes to aggregate components' modes automatic. |
Do Not | Design factored types that have modes. |
Consider | Integrating your aggregate components with Visual Studio Designers. |
Consider | Separating aggregate components and factored types into different assemblies. |
Consider | Exposing access to internal factored types of an aggregate component. |
Do | Use 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>) |
Do | Ensure that the return type of the Begin method implements IAsyncResult. |
Do | Ensure that any by-value and ref parameters of the synchronous method are represented as by-value parameters of the Begin method. |
Do | Ensure that the return type of the End method is the same as the return type of the synchronous method. |
Do | Ensure that any out and ref parameters of the synchronous method are represented as out parameters of the End method. |
Do Not | Continue the asynchronous operation if the Begin method throws an exception. |
Do | Notify 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. |
Do | Throw from the End method to indicate that the asynchronous operation could not complete successfully. |
Do | Complete all remaining work synchronously once the End method is called. |
Consider | Throwing an InvalidOperationException if the End method is called with the same IAsyncResult twice, or if the IAsyncResult was returned from an unrelated Begin method. |
Do | Set IAsyncResult.CompletedSynchronously to true if and only if the async callback will be run on the thread that called Begin. |
Do | Implement the Basic Dispose Pattern on types containing instances of disposable types. |
Do | Implement the Basic Dispose Pattern and provide a finalizer on types holding resources that need to be freed explicitly and that do not have finalizers. |
Consider | Implementing the Basic Dispose Pattern on classes that themselves don't hold unmanaged resources or disposable objects but are likely to have subtypes that do. |
Do | Declare a protected virtual void Dispose(bool disposing) method to centralize all logic related to releasing unmanaged resources. |
Do | Implement the IDisposable interface by simply calling Dispose(true) followed by GC.SupressFinalize(this). |
Do Not | Make the parameterless Dispose method virtual. |
Do Not | Declare any overloads of the Dispose method other than Dispose() and Dispose(bool). |
Do | Allow the Dispose(bool) method to be called more than once. |
Avoid | Throwing an exception from within Dispose(bool) except under critical situations where the containing process has been corrupted (leaks, inconsistent shared state, etc.). |
Do | Throw an ObjectDisposedException from any member that can not be used after the object has been disposed. |
Consider | Providing method Close(), in addition to the Dispose(), if close is standard terminology in the area. |
Avoid | Making types finalizable. |
Do Not | Make value types finalizable. |
Do | Make a type finalizable, if the type is responsible for releasing an unmanaged resource that does not have its own finalizer |
Do | Implement the Basic Dispose Pattern on every finalizable type. |
Do Not | Access any finalizable objects in the finalizer code path, as there is significant risk that they will have already been finalized. |
Do | Make your Finalize method protected. |
Do Not | Let exceptions escape from the finalizer logic, except for system-critical failures. |
Consider | Creating 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. |
Do | Prefer constructors to factories, as they are generally more usable, consistent, and convenient than specialized construction mechanisms. |
Consider | Using a factory if you need more control than can be provided by constructors of the creation of the instances. |
Do | Use a factory in cases where a developer might not know which type to construct, such as wen coding against a base type or interface. |
Consider | Using a factory if having a named method is the only way to make the operation self-explanatory. |
Do | Use a factory for conversion-style operations. |
Do | Prefer implementing factory operations as methods, rather than properties. |
Do | Return created instances as method return values, not as out parameters. |
Consider | Naming factory methods by concatenating Create and the name of the type being created. |
Consider | Naming factory types by concatenating the name of the type being created and Factory. |
Consider | Using the Optional Feature Pattern for optional features in abstractions. |
Do | Provide a simple Boolean property that clients can use to determine whether an optional feature is supported. |
Do | Use virtual methods on the base class that throw NotSupportedException to define optional features. |
Avoid | Making public members virtual. |
Consider | The Template Method Pattern to provide more controlled extensibility. |
Consider | Naming protected virtual members that provide extensibility points for nonvirtual members by suffixing the nonvirtual member name with “Core”. |
Do | Prefer method parameters as the mechanism for users to provide timeout time. |
Do | Prefer using TimeSpan to represent timeout time. |
Do | Throw System.TimeoutException when a timeout elapses. |
Do Not | Return error codes to indicate timeout expiration. |