Everything is local in object orientated systems, that is, if the current object does not have the required context at hand, it is a pity to integrate it later on. The recurring pattern here is to add yet another parameter to a bunch of methods so that the new required context is available.
This might seem unusual, but sometimes, for heavily nested generators for example, I favor context stacks instead. This works only for environments where an object can only be accessed by one thread at a time (which should be the default choice). Using the [ThreadStatic] attribute on static fields in combination with static generic classes, a thread local and type dependent context stack can be implemented in a very cheap way:
[The implementation requires DisposeAction ]
public static class Context<TypeT> { public static IDisposable push(TypeT value) { return Stack.push(value); } public static TypeT Current { get { return Stack.Current; } } static ContextStack Stack { get { return StackInternal ?? (StackInternal = new ContextStack()); }} [ThreadStatic] static ContextStack StackInternal; sealed class ContextStack { readonly Stack<TypeT> Stack = new Stack<TypeT>(); readonly IDisposable PopAction; public ContextStack() { PopAction = new DisposeAction(pop); } public IDisposable push(TypeT value) { Stack.Push(value); return PopAction; } void pop() { Debug.Assert(Stack.Count != 0); Stack.Pop(); } public TypeT Current { get { Debug.Assert(Stack.Count != 0); return Stack.Peek(); } } } } // handy shortcut public static class Context { public static IDisposable push<TypeT>(TypeT value) { return Context<TypeT>.push(value); } }
Now instead of adding a parameter, you may simply be able to set up a context at any time.
using(Context.push(new MyContext())) { callSomeDeeplyNestedMethods(); }
And at any time, in some of the nested methods, call
var myContext=Context<MyContext>.Current;
to retrieve the context.
The context can be nested, and the context stack of different types can be used without interfering.
Edited: The initialization of a [ThreadStatic] must be lazy, otherwise a second thread would only see a null pointer.