A new namespace should be added when new types are being added that have an architectural distinction from the other types in the system. If you are introducing a new feature area to the system it should probably go into a new namespace. If types should have an architectural relationship that should be expressed a on a layer diagram (type(s) A can / cannot depend on type(s) B) then you should create a new namespace.
Namespaces are not to help you as the developer organize the files in the solution in the project explorer. Do not add folders / namespaces unless they are creating a real separation of functionality. Adding a namespace should require to update Architecture Diagrams to define what is allowed to use and not use the new namespace and what your new namespace is allowed to use and not use. In addition to looking at what your namespace references, also look at who references your namespace. If all or at least most of the clients of the namespace need both namespaces, then you shouldn’t create a separate namespace. If your new namespace has the same rules as the parent namespace then it probably should not exist.
Treat every line of code as having the potential to throw an exception. Code should be written such that exceptions cleanly abort the current operation, leaving all data structures in a consistent state that is balanced with respect to paired operations (e.g. init/cleanup).
When possible, defer connecting new data structures to existing data structures until the new structures are fully constructed. When necessary, use try/finally, try/catch/throw, or using/dispose to back out incomplete operations, to release resources deterministically when required, or to ensure that paired operations remain balanced. Consider implementing IDisposable
when it would enable client code to be written more simply via the using statement.
This guideline can be bypassed in specific cases when it requires complexity that is not worthwhile given the specific consequences and probability of failure. These cases should be justified in code comments, or in this document if the situation is sufficiently common.
Rethrowing Exceptions
When re-throwing the same exception object you have caught, use the general throw; statement rather than explicitly throwing the reference you caught. Re-throwing a reference explicitly causes the stack trace to reset. For example:
try
{
// Do work
}
catch (MyExceptionType1 e)
{
throw e; // Incorrect
}
try
{
// Do work
}
catch (MyExceptionType2 e)
{
throw; // Correct
}
Catching Exceptions
Catch in moderation. Generally, the only places a catch without a rethrow are needed are at:
Microsoft’s Exception Guidelines can be found here. The importance and relevance of these guidelines vary, but they are worth reading.
When defining an event that a C# class can raise, follow these steps.
System.EventArgs
for the event parameter
The parameter passed to the event should inherit from EventArgs
, and should in general be a data class with a bunch of properties:using System;
public class MyEventArgs : EventArgs
{
public int SomeValue { get; set; }
public string SomeOtherValue { get; set; }
}
public class HandleMyEvent
{
public event EventHandler<MyEventArgs> SomethingCool;
}
public class MyEventHandlerClass
{
public event EventHandler<MyEventArgs> SomethingCool;
protected virtual void OnSomethingCool(MyEventArgs e)
{
// Take a snapshot to avoid a race condition between the null check and the trigger
EventHandler<MyEventArgs> temp = SomethingCool;
if (temp != null)
{
temp(this, e);
}
}
}
The above examples only work for events that are registered, unregistered, and triggered from a single thread. If you plan to use events in a multi-threaded context, you must:
Here is an example of a way that a multi-threaded event might be implemented. It has the following rules:
public class ClassContainingThreadSafeEvents
{
private readonly Object _eventLock = new Object();
// Note: no "event" keyword
private EventHandler<MyEventArgs> _internalHandler;
public event EventHandler<MyEventArgs> SomeEvent
{
add
{
lock(_eventLock) { _internalHandler += value; }
}
remove
{
lock(_eventLock) { _internalHandler -= value; }
}
}
protected virtual void OnSomeEvent(MyEventArgs e)
{
lock(_eventLock)
{
if(_internalHandler != null)
{
_internalHandler(this, e);
}
}
}
}