Академический Документы
Профессиональный Документы
Культура Документы
Page 1 of
40
10
11
Table of Contents
1.
DESIGN GUIDE.................................................................................................................................................................... 5
1.1
Abstractions................................................................................................................................................................. 5
1.2
Inheritance vs. Helpers................................................................................................................................................... 5
1.3
Interfaces vs. Abstract Classes.......................................................................................................................................... 5
1.4
Modifying interfaces....................................................................................................................................................... 6
1.5
Delegates vs. Interfaces.................................................................................................................................................. 6
1.6
Methods vs. Properties.................................................................................................................................................... 6
1.7
Virtual Methods............................................................................................................................................................ 7
1.8
Choosing Types............................................................................................................................................................. 7
1.9
Design-by-Contract........................................................................................................................................................ 7
Object Composition.................................................................................................................................................................................... 7
Object Model & API Design............................................................................................................................................................................ 8
1.12
Asynchronous Programming.............................................................................................................................................. 8
1.13
Multi-Threading............................................................................................................................................................ 8
2
NAMESPACES..................................................................................................................................................................... 9
2.1
2.2
Usage......................................................................................................................................................................... 9
Naming..................................................................................................................................................................... 10
FORMATTING................................................................................................................................................................... 10
3.1
1.1.1
3.2
3.3
3.4
3.5
3.6
3.7
3.8
3.9
3.10
3.11
3.11.1
3.11.2
3.11.3
4
NAMING........................................................................................................................................................................... 15
4.1
4.1.1
4.1.2
4.1.3
4.1.4
1.1.2
4.1.4.1
4.1.4.2
4.1.4.3
4.1.5
4.1.5.1
4.1.5.2
4.1.5.3
4.1.5.4
4.1.5.5
4.1.5.6
4.1.5.7
4.1.5.8
4.1.5.9
4.1.5.10
4.1.5.11
Basic Composition........................................................................................................................................................ 15
Valid Characters.......................................................................................................................................................... 15
General Rules............................................................................................................................................................. 16
Collision and Matching................................................................................................................................................... 16
Capitalization............................................................................................................................................................. 16
Capitalization Styles.................................................................................................................................................. 16
Pascal Casing.............................................................................................................................................................. 16
Camel Casing.............................................................................................................................................................. 17
Upper case................................................................................................................................................................. 17
Naming Guidelines....................................................................................................................................................... 17
Class Naming Guidelines................................................................................................................................................ 17
Interface Naming Guidelines........................................................................................................................................... 17
Enum Naming Guidelines............................................................................................................................................... 18
ReadOnly and Const Field Names..................................................................................................................................... 18
Parameter/non const field Names..................................................................................................................................... 18
Variable Names........................................................................................................................................................... 18
Method Names............................................................................................................................................................ 18
Property Names........................................................................................................................................................... 19
Event Names.............................................................................................................................................................. 20
UI Controls Naming convention........................................................................................................................................ 20
Capitalization summary................................................................................................................................................. 21
DECLARATIONS.................................................................................................................................................................. 24
5.1
5.2
5.3
STATEMENTS..................................................................................................................................................................... 26
6.1
6.2
Simple Statements....................................................................................................................................................... 26
Return Statements....................................................................................................................................................... 26
RESOURCE CLEANUP.......................................................................................................................................................... 29
8.1
8.2
8.3
8.4
Exception Throwing...................................................................................................................................................... 26
Exception Handling...................................................................................................................................................... 27
Try-finally Block.......................................................................................................................................................... 29
Basic Dispose Pattern.................................................................................................................................................... 30
Finalizable Types......................................................................................................................................................... 34
Overriding Dispose....................................................................................................................................................... 38
1. DESIGN GUIDE
In general, design decisions that involve thinking about the following topics should not be made alone. You should apply these principles to come up
with a design, but should always seek the advice and approval of at least one other team member before proceeding. It is easy to come up with a
document, but the spirit of best coding standards comes from the developers, who adopt the following recommendations.
1.1
ABSTRACTIONS
The first rule of design is dont overdesign. Overdesign leads to a framework that offers unused functionality and has interfaces that are
difficult to understand and implement. Only create abstractions where there will be more than one implementation or where there is a
reasonable need to provide for other implementations in the future.
This leads directly to the second rule of design: dont under-design. Understand your problem domain well enough before starting to code so
that you accommodate reasonably foreseeable additional requirements. For example, whether or not there is a need for multiple
implementations (in which case you should define interfaces) or a need for code sharing (in which case abstract interfaces are in order). You
should create abstractions where they prevent repeated code (applying the DRY principle) or where they provide decoupling.
If you do create an abstraction, make sure that there are tests which run against the abstraction rather than a concrete implementation so
that all future implementations can be tested. For example, database access for a particular database should include an abstraction and
tests for that abstraction that can be used to verify all supported databases.
1.2
1.3
Where interfaces can be useful is in restricting write-access to certain properties or containers. That is, an interface can be declared with
only a getter, but the implementation includes both a getter and setter. This allows an application to set the property when it works with an
internal implementation, but to restrict the code receiving the interface to a read-only property.
1.4
MODIFYING INTERFACES
In general, be extremely careful of modifying interfaces that are used by code not under your control (i.e. code that has shipped and been
integrated into other codebases). If a change needs to be made, it must be very clearly documented in the release notes for the code and
must include tips for implementing / updating the implementation for the interface.
Another solution is to develop a parallel path that consumes a new interface inherited from the existing one.
1.5
1.6
Properties dont require parentheses and result in cleaner code when called (especially when many are chained together)
It clearly indicates that the value is a logical property of the construct instead of an operation.
1.7
VIRTUAL METHODS
Choose carefully which methods are marked as virtual as they incur design, test and maintenance costs not shared by non-virtual methods.
1.8
CHOOSING TYPES
Use the least-derived possible type for local variables and method parameters; this makes the expected API as explicit and open as possible.
Use existing interfaces wherever possible, even when declaring local or member variables. Interfaces should be useful in most instances;
otherwise theyve probably been designed poorly.
IMessageStore messages = new MessageStore();
IExpressionContext context = new ExpressionContext(this);
Use the actual instantiated class for the member type when you need to access members not available in the interface. Do not modify the
interface solely in order to keep using the interface instead of the class.
1.9
DESIGN-BY-CONTRACT
Use assertions at the beginning of a method to assert preconditions; assert post-conditions if appropriate.
Use Debug.Assert or throw standard system exceptions for pre- and post-conditions
You may throw the exception on the same line as the check, to mirror the formatting of the assertion.
if (connection == null) { throw new ArgumentNullException("connection"); }
1.10
OBJECT COMPOSITION
Always declare types explicitly within a namespace. Do not use the default {global} namespace.
Avoid overuse of the public access modifier. Typically fewer than 10% of your types and members will be
Part of a public API, unless you are writing a class library.
Consider using internal or private access modifiers for types and members unless you intend to support
Them as part of a public API.
Never use the protected access modifier within sealed classes unless overriding a protected member of
An inherited type.
Avoid declaring methods with more than 5 parameters. Consider refactoring this code.
Try to replace large parameter-sets (> than 5 parameters) with one or more class or struct parameters
Especially when used in multiple method signatures.
Do not use the new keyword on method and property declarations to hide members of a derived type.
Only use the base keyword when invoking a base class constructor or base implementation within an
Override.
Consider using method overloading instead of the params attribute (but be careful not to break CLS
Compliance of your APIs).
Always validate an enumeration variable or parameter value before consuming it. They may contain any value
That the underlying Enum type (default int) supports.
Example
public void Test(BookCategory cat)
{
if (Enum.IsDefined(typeof(BookCategory), cat))
{}
}
1.11
1.12
ASYNCHRONOUS PROGRAMMING
1.13
Avoid async void. Prefer async Task methods over async void methods
Async all the way. Dont mix blocking and async code
Configure context. Use ConfigureAwait(false) when you can
MULTI-THREADING
Don't use Thread.Abort to terminate other threads. Calling Abort on another thread is akin to throwing an exception on that thread,
without knowing what point that thread has reached in its processing.
Don't use Thread.Suspend and Thread.Resume to synchronize the activities of multiple threads. Do use Mutex, ManualResetEvent,
AutoResetEvent, and Monitor.
Don't control the execution of worker threads from your main program (using events, for example). Instead, design your program so that
worker threads are responsible for waiting until work is available, executing it, and notifying other parts of your program when finished. If
your worker threads do not block, consider using thread pool threads. Monitor.PulseAll is useful in situations where worker threads block.
Don't use types as lock objects. That is, avoid code such as lock(typeof(X)) in C# or SyncLock(GetType(X)) in Visual Basic, or the use of
Monitor.Enter with Type objects. For a given type, there is only one instance of System.Type per application domain. If the type you take a
lock on is public, code other than your own can take locks on it, leading to deadlocks. For additional issues, see Reliability Best Practices.
Use caution when locking on instances, for example lock(this) in C# or SyncLock(Me) in Visual Basic. If other code in your application,
external to the type, takes a lock on the object, deadlocks could occur.
Do ensure that a thread that has entered a monitor always leaves that monitor, even if an exception occurs while the thread is in the
monitor. The C# lock statement and the Visual BasicSyncLock statement provide this behavior automatically, employing a finally block to
ensure that Monitor.Exit is called. If you cannot ensure that Exit will be called, consider changing your design to use Mutex. A mutex is
automatically released when the thread that currently owns it terminates.
Do use multiple threads for tasks that require different resources, and avoid assigning multiple threads to a single resource. For example,
any task involving I/O benefits from having its own thread, because that thread will block during I/O operations and thus allow other
threads to execute. User input is another resource that benefits from a dedicated thread. On a single-processor computer, a task that
involves intensive computation coexists with user input and with tasks that involve I/O, but multiple computation-intensive tasks contend
with each other.
NAMESPACES
2.1
USAGE
Do not use the global namespace; the only exception is for ASP.NET pages that are generated into the global namespace.
Avoid fully-qualified type names; use the using statement instead.
If the IDE inserts a fully-qualified type name in your code, you should fix it. If the unadorned name conflicts with other already-included
namespaces, make an alias for the class with a using clause
Avoid putting a using statement inside a namespace (unless you must do so to resolve a conflict)
2.2
NAMING
Avoid deep namespace-hierarchies (five or more levels) as that makes it difficult to browse and understand.
Avoid making too many namespaces; instead, use catch-all namespace suffixes, like Utilities, Core or General until it is
clearer whether a class or group of classes warrant their own namespace. Refactoring is your friend here.
Do not include the version number in a namespace name.
Use long-lived identifiers in a namespace name.
Namespaces should be plural, as they will contain multiple types
If your framework or application encompasses more than one tier, use the same namespace identifiers for similar tasks. For example,
common data-access code goes in <Application>.Data, but metadata-based data-access code goes in <Application>.<MetaName>.Data
Avoid using reserved namespace names like System because these will conflict with standard .NET namespaces and require resolution
using the global:: namespace prefix.
FORMATTING
The formatting rules were designed for use with C#. Where possible, they should be applied to other languages (CSS, JavaScript, etc.) as well.
3.1
3.2
COMMENTS
BLOCK COMMENTS
Block comments should usually be avoided. For descriptions use of the /// comments to give C# standard descriptions is recommended.
When you wish to use block comments you should use the following style :
/* Line 1
* Line 2
* Line 3
*/
As this will set off the block visually from code for the (human) reader. Alternatively you might use this old fashioned C style for single line
comments, even though it is not recommended. In case you use this style, a line break should follow the comment, as it is hard to see code
proceeded by comments in the same line:
/* blah blah blah */
Block comments may be useful in rare cases, refer to the TechNote 'The fine Art of Commenting' for an example. Generally block comments
are useful for comment out large sections of code.
3.4
3.5
DOCUMENTATION COMMENTS
In the .net framework, Microsoft has introduced a documentation generation system based on XML comments. These comments are formally
single line C comments containing XML tags. They follow this pattern for single line comments:
/// <summary>
/// This is a comment for class
/// </summary>
Multiline XML comments follow this pattern:
///<exception cref="testException">
///This is an exception comment
///thrown for test exception
///</exception>
All lines must be preceded by three slashes to be accepted as XML comment lines. Tags fall into two categories:
Documentation items
Formatting/Referencing
The first category contains tags like <summary>, <param> or </exception>. These represent items that represent the elements of a program's
API which must be documented for the program to be useful to other programmers. These tags usually have attributes such as name or cref
as demonstrated in the multiline example above. These attributes are checked by the compiler, so they should be valid.
The latter category governs the layout of the documentation, using tags such as <code>, <list> or <para>.
Documentation can then be generated using the 'documentation' item in the #develop 'build' menu. The documentation generated is in
HTMLHelp format.
3.6
3.7
BRACES
Curly braces shouldwith a few exceptions outlined belowgo on their own line
A line with only a closing brace should never be preceded by an empty line
A line with only an opening brace should never be followed by an empty line
PROPERTIES
3.8
Simple getters and setters should go on the same line as all brackets
Abstract properties should have get, set and all braces on the same line
Complex getters and setters should have each bracket on its own line
Prefer automatic properties as it saves a lot of typing and vastly improves readability
METHODS
Completely empty functions, like constructors, should have a space between brackets placed on the same line:
SomeClass(string name)
: base(name)
{}
3.9
ENUMERATIONS
Use the trailing comma for the last member of an enumeration; this makes it easier to move them around, if needed.
Page 10 of
40
3.10
PARENTHESES
Prefix operators (e.g. !) and method calls should not have parentheses around them.
return !HasValue ? Value.ToString() : "EMPTY";
3.11
EMPTY LINES
In the following list, the phrase surrounding code refers to a line consisting of more than just an opening or closing brace. That is, no
new line is required when an element is at the beginning or end of a methods or other block-level element.
Always place an empty line in the following places:
Between the file header and the namespace declaration or the first using statement.
Between the last using statement and the namespace declaration.
Between types (classes, structs, interfaces, delegates or enums).
Between public, protected and internal members.
Between preconditions and ensuing code.
Between post-conditions and preceding code.
Between a call to a base method and ensuing code.
Between return statements and surrounding code (this does not apply to return statements at the beginning or end of methods)
Between block constructs (e.g. while loops or switch statements) and surrounding code.
Page 11 of
40
Page 12 of
40
Between logical groups of code in a method; this notion is subjective and more a matter of style. You should use empty lines to
improve readability, but should not overuse them.
Between the last line of code in a block and a comment for the next block of code.
Between statements that are broken up into multiple lines.
Between a #region tag and the first line of code in that region.
Between the last line of code in a region and the #endregion tag.
3.12
Between any line and a line that has only an opening or closing brace on it (i.e. there should be no leading or trailing newlines in a block)
Between undocumented fields (usually private); if there are many such fields, you may use empty lines to group them by purpose.
LINE BREAKING
Blank lines improve readability. They set off blocks of code which are in themselves logically related. Two blank lines should always be used
between:
Logical sections of a source file
Class and interface definitions (try one class/interface per file to prevent this case)
One blank line should always be used between:
Methods
Properties
Local variables in a method and its first statement
Logical sections inside a method to improve readability
Note that blank lines must be indented as they would contain a statement this makes insertion in these lines much easier.
3.13
INTER-TERM SPACING
There should be a single space after a comma or a semicolon,
For example:
Don't use :
void TestMethod(a,b,c)
or
void TestMethod( a, b, c )
Recommended to Use :
void TestMethod(a, b, c)
3.14
3.15
No line should exceed 100 characters; use the line-breaking rules listed below to break up a line.
Use line-breaking only when necessary; do not adopt it as standard practice.
If one or more line-breaks is required, use as few as possible
Line-breaking should occur at natural boundaries; the most common such boundary is between parameters in a method call or definition.
Lines after such a line-break at such a boundary should be indented.
The separator (e.g. a comma) between elements formatted onto multiple lines goes on the same line after the element; the IDE is much
more helpful when formatting that way.
The most natural line-breaking boundary is often before and after a list of elements. For example, the following method call has linebreaks at the beginning and end of the parameter list.
people.DataSource = CurrentCompany.Employees.GetList(
connection, metaClass, GetFilter(), null
);
If one of the parameters is much longer, then you add line-breaking between the parameters; in that case, all parameters
are formatted onto their own lines:
people.DataSource = CurrentCompany.Employees.GetList(
connection,
metaClass,
GetFilter("Global.Applications.Updater.PersonList.Search"),
null
);
Note in the examples above that the parameters are indented. If the assignment or method call was longer, they would no longer fit on the
same line. In that case, you should use two levels of indenting.
Application.Model.people.DataSource =
Global.ApplicationEnvironment.CurrentCompany.Employees.GetList(
connection,
metaClass,
GetFilter("Global.Applications.Updater.PersonList.Search"),
null
);
If there is a logical grouping for parameters, you may apply line-breaking at those boundaries instead (breaking the all-on-one-line or eachon-its-own-line rule stated above). For example, the following method specifies Cartesian coordinates:
Geometry.PlotInBox(
"Global.Applications.MainWindow",
topLeft.X, topLeft.Y,
bottomRight.X, bottomRight.Y
);
NAMING
The naming rules were designed for use with C#. Where possible, they should be applied to elements of other languages (CSS,
JavaScript, etc.) as well. Follow all .NET Framework Design Guidelines for both internal and external members. Highlights of these
include:
The reasons to extend the public rules( no Hungarian, no prefix for member variables, etc.) are to produce a consistent source code
appearance. In addition a goal is to have clean readable source. Code legibility should be a primary goal.
4.1
BASIC COMPOSITION
VALID CHARACTERS
4.3
GENERAL RULES
4.4
4.5
You may not use identifiers that are keywords in C#; neither may you use the @-symbol to turn a keyword into a valid identifier.
Do not name an element with the same identifier as its containing element (e.g. dont create a static class named Expressions within a
namespace called Expressions)
Since C# allows it, you should use the same identifier for a property as its type if that is the most appropriate name in that context
(this is often the case with enum properties)
CAPITALIZATION
The following table lists the capitalization and naming rules for different language elements. Pascal-case capitalizes every individual word
within an identifier, including the first one. Camelcase capitalizes all but the first word in an identifier.
4.6
PASCAL CASING
CAPITALIZATION STYLES
This convention capitalizes the first character of each word (as in TestCounter).
4.8
CAMEL CASING
This convention capitalizes the first character of each word except the first one. E.g. testCounter.
4.9
UPPER CASE
Only use all upper case for identifiers if it consists of an abbreviation which is one or two characters long, identifiers of three or more
characters should use Pascal Casing instead.
For Example:
public class Math
{
public const double PI =
3.14; public const string E =
"asdf";
public const int feigenBaumNumber = 91;
}
4.10
NAMING GUIDELINES
Generally the use of underscore characters inside names and naming according to the guidelines for Hungarian notation are considered bad
practice. Hungarian notation is a defined set of pre and postfixes which are applied to names to reflect the type of the variable. This style
of naming was widely used in early Windows programming, but now is obsolete or at least should be considered deprecated. Using
Hungarian notation is not allowed if you follow this guide.
4.11
If a class implements a single interface, it should reflect this by incorporating the interface name into its own (e.g. MetaList
implements IList).
Static classes used as toolkits of static functions should use the suffix Tools and should go in a file ending in Tools.cs.
4.12
Name interfaces with nouns or noun phrases or adjectives describing behavior. (Example IComponent or IEnumberable)
4.13
4.14
Do use descriptive names, which should be enough to determine the variable meaning and its type. But prefer a name thats based on
the parameters meaning.
Use Camel Casing
Prefer whole words instead of abbreviations (use index instead of idx).
Parameter names should be based on their intended use or purpose rather than their type (unless the type indicates the purpose
adequately).
Do not simply repeat the type for the parameter name; use a name that is as short as possible, but doesnt lose meaning. (E.g. a
parameter of type IDataContext should be called context instead of dataContext.
However, if the method also, at some point, makes use of an IViewContext, you should
make the parameter name more specific, using dataContext instead.
VARIABLE NAMES
4.17
Name static fields with nouns, noun phrases or abbreviations for nouns
Use Pascal Casing
4.16
Use Pascal Casing for enum value names and enum type names
Dont prefix (or suffix) a enum type or enum values
Use singular names for enums
Use plural name for bit fields.
4.15
Counting variables are preferably called i, j, k, l, m, n when used in 'trivial' counting loops.
Use Camel Casing
METHOD NAMES
Method names should not repeat information from the enclosing type. For example, an interface named IMessages should not have a
method named LogMessage; instead name the method Log.
State what a method does; do not describe the parameters (let code-completion and the signature do that for you).
Methods that return values should indicate this in their name, like GetList(), GetItem() or CreateDefaultDatabase(). Though there is
garbage collection in C#, you should still use Get to indicate retrieval of a local value and Create to indicate a factory method, which
always creates a new reference. For example, instead of writing:
public IDataList<GenericObject> GetList(IMetaClass cls)
{
return
ViewApplication.Application.CreateContext<GenericObject>(cls);
}
You should write:
public IDataList<GenericObject> CreateList(IMetaClass cls)
{
return
ViewApplication.Application.CreateContext<GenericObject>(cls);
}
4.18
Avoid defining everything as a noun or a manager. Prefer names that are logically relevant, like Missile.Launch() rather than
MissileLauncher.Execute(missile).
Methods that set a single property value should begin with the verb Set.
PROPERTY NAMES
Although its a property not a method, the first example might still be interpreted as a verb rather than an adjective. The second example
adds the verb Is to avoid confusion, but both formulations are acceptable.
A propertys backing field (if present) must be an underscore followed by the name of the property in camel case.
Use common names, like Item or Value, for accessing the central property of a type
4.19
4.20
Do not include type information in property names. For example, for a property of type IMetaRelation, use the name Relation instead
of the name MetaRelation
Make the identifier as short as possible without losing information. For example, if a class named IViewContext has a property of type
IViewContextHandler, that property should be called Handler.
If there are two properties that could be shortened in this way, then neither of them should be. If the class in the example above has
another property of type IEventListHandler, then the properties should be named something like ViewContextHandler and
EventListhandler, respectively.
Avoid repeating information in a class member that is already in the class name. Suppose, there is an interface named IMessages;
instances of this interface are typically named messages. That interface should not have a property named Messages because that
would result in calls to messages.Messages.Count, which is redundant and not very readable. Instead, name the property something
more semantically relevant, like All, so the call would read messages.All.Count.
EVENT NAMES
Prefix
Button
btn
CheckBox
chk
CheckedListBox
lst
ComboBox
cmb
ContextMenu
mnu
DataGrid
dg
DateTimePicker
dtp
Page 20 of
40
Form
suffix: XXXForm
GroupBox
grp
ImageList
iml
Label
lb
ListBox
lst
ListView
lvw
Menu
mnu
MenuItem
mnu
NotificationIcon
nfy
Panel
pnl
PictureBox
pct
ProgressBar
prg
RadioButton
rad
Splitter
spl
StatusBar
sts
TabControl
tab
TabPage
tab
TextBox
tb
Timer
tmr
TreeView
tvw
For example, for the File | Save menu option, the Save MenuItem would be called mnuFileSave.
4.21
CAPITALIZATION SUMMARY
Identifier
Casing
Naming Structure
Class,
Structure
PascalCasing
Noun
Example
public class ComplexNumber {...}
public struct ComplextStruct {...}
Page 20 of
40
Namespace
PascalCasing
Enumeration
PascalCasing
Method
PascalCasing
Public
Property
PascalCasing
Non-public
Field
Event
Noun
Do not use the same name
for a namespace and a type in
that namespace.
Noun
Do name flag enums with
plural nouns or noun phrases
and simple enums with
singular nouns or noun
phrases.
Verb or Verb phrase
namespace
Microsoft.Sample.Windows7
[Flags]
public enum ConsoleModifiers
{ Alt, Control }
Noun or Adjective
Do name collection
proprieties with a plural
phrase describing the items in
the collection, as opposed to
a singular phrase followed by
List or Collection.
Do name Boolean
proprieties with an
affirmative phrase (CanSeek
instead of CantSeek).
Optionally, you can also
prefix Boolean properties with
Is, Can, or Has but only
where it adds value.
camelCasing or
Noun or Adjective.
_camelCasing Do be consistent in a code
sample when you use the '_'
prefix.
PascalCasing
Page 20 of
40
tense.
Do not use Before or
After prefixes or postfixes
to indicate pre and post
events.
Do add the suffix
EventHandler to names of
delegates that are used in
events.
Do add the suffix
Callback to names of
delegates other than those
used as event handlers.
Do not add the suffix
Delegate to a delegate.
Delegate
PascalCasing
Interface
PascalCasing
I prefix
Noun
Constant
PascalCasing for
publicly visible;
camelCasing for
internally
visible;
All capital only
for abbreviation
of one or two
chars long.
Noun
Parameter,
Variable
camelCasing
Noun
int customerID;
Generic
Type
Parameter
PascalCasing
T prefix
Noun
Do name generic type
parameters with descriptive
names, unless a single-letter
name is completely selfexplanatory and a descriptive
name would not add value.
public delegate
WindowClosedEventHandler
T, TItem, TPolicy
Page 20 of
40
Resource
PascalCasing
ArgumentExceptionInvalidName
DECLARATIONS
5.1
5.2
INITIALIZATION
Try to initialize local variables as soon as they are declared. For example:
Page 20 of
40
No space between a method name and the parenthesis "(" starting its parameter list.
The opening brace "{" appears in the next line after the declaration statement.
The closing brace " }" starts a line by itself indented to match its corresponding opening brace.
For example :
class MySample : MyClass, IMyInterface
{
int myInt;
public MySample(int myInt)
{
this.myInt = myInt;
}
void Inc()
{
++myInt;
}
void EmptyMethod()
{
}
}
STATEMENTS
6.1
SIMPLE STATEMENTS
Each line should contain only one statement.
6.2
RETURN STATEMENTS
A return statement should not use outer most parentheses.
Don't use:
return (n * (n + 1) / 2);
Recommended to Use:
return n * (n + 1) / 2;
EXCEPTION THROWING
Do report execution failures by throwing exceptions. Exceptions are the primary means of reporting errors in frameworks. If a member
cannot successfully do what it is designed to do, it should be considered an execution failure and an exception should be thrown. Do not
return error codes.
Do throw the most specific (the most derived) exception that makes sense. For example, throw ArgumentNullException and not its base type
ArgumentException if a null argument is passed. Throwing System.Exception as well as catching System.Exception is nearly always the wrong
thing to do.
Do not use exceptions for the normal flow of control, if possible. Except for system failures and operations with potential race conditions,
you should write code that does not throw exceptions. For example, you can check preconditions before calling a method that may fail and
throw exceptions. For example,
// C# sample:
if (collection != null && !collection.IsReadOnly)
{
collection.Add(additionalNumber);
}
Do not throw exceptions from exception filter blocks. When an exception filter raises an exception, the exception is caught by the CLR,
and the filter returns false. This behavior is indistinguishable from the filter executing and returning false explicitly and is therefore very
difficult to debug.
' VB.NET sample
' This is bad design. The exception filter (When clause)
' may throw an exception when the InnerException property
' returns null
Try
...
Catch e As ArgumentException _
When e.InnerException.Message.StartsWith("File")
...
End Try
Do not explicitly throw exceptions from finally blocks. Implicitly thrown exceptions resulting from calling methods that throw are
acceptable.
7.2
EXCEPTION HANDLING
You should not swallow errors by catching nonspecific exceptions, such as System.Exception, System.SystemException, and so on in .NET
code. Do catch only specific errors that the code knows how to handle. You should catch a more specific exception, or re-throw the general
exception as the last statement in the catch block. There are cases when swallowing errors in applications is acceptable, but such cases are
rare.
Good:
try
{
...
}
catch(System.NullReferenceException exc)
{
...
}
catch(System.ArgumentOutOfRangeException
{
...
exc)
}
catch(System.InvalidCastException exc)
{
...
}
Bad:
try
{
...
}
catch (Exception ex)
{
...
}
Do prefer using an empty throw when catching and re-throwing an exception. This is the best way to preserve the exception call stack.
Good:
try
{
... // Do some reading with the file
}
catch
{
file.Position = position; // Unwind on failure
throw; // Rethrow
}
Bad:
try
{
... // Do some reading with the file
}
}
8
RESOURCE CLEANUP
Do not force garbage collections with GC.Collect.
8.1
TRY-FINALLY BLOCK
Do use try-finally blocks for cleanup code and try-catch blocks for error recovery code. Do not use catch blocks for cleanup code. Usually,
the cleanup logic rolls back resource (particularly, native resource) allocations. For example,
FileStream stream = null;
try
{
stream = new FileStream(...);
...
}
finally
{
if (stream != null)
{
stream.Close();
}
}
C# and VB.NET provide the using statement that can be used instead of plain try-finally to clean up objects implementing the
IDisposable interface.
// C# sample:
using (FileStream stream = new FileStream(...))
{
...
Many language constructs emit try-finally blocks automatically for you. Examples are C#/VBs using statement, C#s lock
statement, VBs SyncLock statement, C#s foreach statement, and VBs For Each statement.
8.2
Page 30 of
40
Page 30 of
40
disposed = true;
}
Do implement the Basic Dispose Pattern on types containing instances of disposable types.
Do extend the Basic Dispose Pattern to provide a finalizer on types holding resources that need to be freed explicitly and that do not have
finalizers. For example, the pattern should be implemented on types storing unmanaged memory buffers.
You should implement the Basic Dispose Pattern on classes that themselves dont hold unmanaged resources or disposable objects but are
likely to have subtypes that do. A great example of this is the System.IO.Stream class. Although it is an abstract base class that doesnt hold
any resources, most of its subclasses do and because of this, it implements this pattern.
Do declare a protected virtual void Dispose(bool disposing) method to centralize all logic related to releasing unmanaged resources. All
resource cleanup should occur in this method. The method is called from both the finalizer and the IDisposable.Dispose method. The
Page 31 of
40
parameter will be false if being invoked from inside a finalizer. It should be used to ensure any code running during finalization is not accessing
other finalizable objects. Details of implementing finalizers are described in the next section.
protected virtual void Dispose(bool disposing)
Page 32 of
40
{
// Protect from being called multiple times.
if (disposed)
{
return;
}
if (disposing)
{
// Clean up all managed resources.
if (resource != null)
{
resource.Dispose();
}
}
disposed = true;
}
Do implement the IDisposable interface by simply calling Dispose(true) followed by GC.SuppressFinalize(this). The call to SuppressFinalize
should only occur if Dispose(true) executes successfully.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
Do not make the parameterless Dispose method virtual. The Dispose(bool) method is the one that should be overridden by subclasses.
You should not throw an exception from within Dispose(bool) except under critical situations where the containing process has been
corrupted (leaks, inconsistent shared state, etc.). Users expect that a call to Dispose would not raise an exception. For example, consider
the manual try-finally in this C# snippet:
// Do some stuff
}
finally
{
tr.Dispose();
// More stuff
}
If Dispose could raise an exception, further finally block cleanup logic will not execute. To work around this, the user would need to wrap
every call to Dispose (within their finally block!) in a try block, which leads to very complex cleanup handlers. If executing a Dispose(bool
disposing) method, never throw an exception if disposing is false. Doing so will terminate the process if executing inside a finalizer context.
Do throw an ObjectDisposedException from any member that cannot be used after the object has been disposed.
public class DisposableResourceHolder : IDisposable
{
private bool disposed = false;
private SafeHandle resource; // Handle to a resource
public void DoSomething()
{
if (disposed)
{
throw new ObjectDisposedException(...);
}
// Now call some native methods using the resource
...
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
{
return;
// Cleanup
...
disposed = true;
}
}
8.3
FINALIZABLE TYPES
Finalizable types are types that extend the Basic Dispose Pattern by overriding the finalizer and providing finalization code path in
the Dispose(bool) method. The following code shows an example of a finalizable type:
public class ComplexResourceHolder : IDisposable
{
bool disposed = false;
private IntPtr buffer; // Unmanaged memory buffer
private SafeHandle resource; // Disposable handle to a resource
public ComplexResourceHolder()
{
this.buffer = ... // Allocates memory
this.resource = ... // Allocates the resource
}
public void DoSomething()
{
if (disposed)
{
throw new ObjectDisposedException(...);
}
// Now call some native methods using the resource
...
}
~ComplexResourceHolder()
{
Dispose(false);
disposed = true;
}
Do make a type finalizable, if the type is responsible for releasing an unmanaged resource that does not have its own finalizer. When
implementing the finalizer, simply call Dispose(false) and place all resource cleanup logic inside the Dispose(bool disposing) method.
...
~ComplexResourceHolder()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
...
}
}
Do be very careful to make type finalizable. Carefully consider any case in which you think a finalizer is needed. There is a real cost
associated with instances with finalizers, from both a performance and code complexity standpoint.
Do implement the Basic Dispose Pattern on every finalizable type. See the previous section for details on the basic pattern. This gives users
of the type a means to explicitly perform deterministic cleanup of those same resources for which the finalizer is responsible.
You should create and use a critical finalizable object (a type with a type 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 resource wrappers based on SafeHandle or SafeHandleZeroOrMinusOneIsInvalid (for Win32 resource handle whose value of either 0
or -1 indicates an invalid handle) to writing finalizer by you to encapsulate unmanaged resources where possible, in which case a finalizer
becomes unnecessary because the wrapper is responsible for its own resource cleanup. Safe handles implement the IDisposable interface,
and inherit from CriticalFinalizerObject so the finalizer logic will absolutely execute even in the face of forced application domain unloads
and thread aborts.
/// <summary>
/// Represents a wrapper class for a pipe handle.
/// </summary>
[SecurityCritical(SecurityCriticalScope.Everything),
{
}
: base(true)
{
base.SetHandle(preexistingHandle);
}
[ReliabilityContract(Consistency.WillNotCorruptState,
Cer.Success),
DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LocalFree(IntPtr hMem);
Do not access any finalizable objects in the finalizer code path, as there is significant risk that they will have already been finalized. For
example, a finalizable object A that has a reference to another finalizable object B cannot reliably use B in As finalizer, or vice versa.
Finalizers are called in a random order (short of a weak ordering guarantee for critical finalization).
It is OK to touch unboxed value type fields.
Also, be aware that objects stored in static variables will get collected at certain points during an application domain unload or while
exiting the process. Accessing a static variable that refers to a finalizable object (or calling a static method that might use values stored
in static variables) might not be safe if Environment.HasShutdownStarted returns true.
Do not let exceptions escape from the finalizer logic, except for system-critical failures. If an exception is thrown from a finalizer, the
CLR may shut down the entire process preventing other finalizers from executing and resources from being released in a controlled manner.
8.4
OVERRIDING DISPOSE
If you're inheriting from a base class that implements IDisposable, you must implement IDisposable also. Always call your base class's
Dispose(bool) so it cleans up.
public class DisposableBase : IDisposable
{
~DisposableBase()
{
Dispose(false);
}
1.
Page 40 of
40