Full and working extension methods for getting a SynchronizationContext from Thread or ExecutionContext (or null if none), or DispatcherSynchronizationContext from Dispatcher . Tested on .NET 4.6.2 .
using Ectx = ExecutionContext; using Sctx = SynchronizationContext; using Dctx = DispatcherSynchronizationContext; public static class _ext {
All of the above functions end up calling the __get code shown below, which requires some explanation.
Note that __get is a static field pre-initialized by a drop lambda block. This allows us to gently intercept the first caller just to run a one-time initialization, which prepares the tiny and constant replacement delegate much faster and without reflection.
The final act for the fatal initialization effort is to replace the replacement with "__get", which at the same time tragically means that the code discards itself without leaving a trace, and all subsequent callers are directly sent to DynamicMethod , not even a hint of bypass logic.
static Func<Ectx, Sctx> __get = arg => { // Hijack the first caller to do initialization... var fi = typeof(Ectx).GetField( "_syncContext", // private field in 'ExecutionContext' BindingFlags.NonPublic|BindingFlags.Instance); var dm = new DynamicMethod( "foo", // (any name) typeof(Sctx), // getter return type new[] { typeof(Ectx) }, // type of getter single arg typeof(Ectx), // "owner" type true); // allow private field access var il = dm.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, fi); il.Emit(OpCodes.Ret); // ...now replace ourself... __get = (Func<Ectx, Sctx>)dm.CreateDelegate(typeof(Func<Ectx, Sctx>)); // oh yeah, don't forget to handle the first caller request return __get(arg); // ...never to come back here again. SAD! };
The nice part is the very end, where - in order to actually get the value for the pre-rooted first caller - the function supposedly calls itself its own argument, but avoids recursion, replacing itself directly earlier.
There is no particular reason to demonstrate this unusual method for the specific SynchronizationContext problem discussed on this page. Capturing the _syncContext field from an ExecutionContext can be easily and trivially addressed by traditional reflection (plus some method of freezing the extension method). But I thought that I would share this approach, which I personally used for quite some time, because it is also easily adaptable and equally widely applicable to such cases.
This is especially important when extreme performance is required when accessing a non-public field. I think I initially used this on a QPC-based frequency counter, where the field was read in a narrow loop that repeated every 20 or 25 nanoseconds, which would not be possible with normal reflection.
This concludes the main answer, but below I have included some interesting points that are less relevant to the research request, more similar to the technique just demonstrated.
Runtime Challenges
For clarity, I divided the steps of “installing swap” and “first usage” into two separate lines in the code shown above, unlike what I have in my own code (in the next version, the choice from main memory is also avoided compared to previous, potentially implying thread safety, see the detailed discussion below):
return (__get = (Func<Ectx, Sctx>)dm.CreateDel...(...))(arg);
In other words, all callers, including the first, select the value in exactly the same way, and the reflection code is never used for this. It records only the getter replacement. Provided by il-visualizer , we can see the body of this DynamicMethod in the debugger at runtime:

Non-latching security
I should note that replacing in the body of the function is a completely thread safe operation, given the .NET memory model and the philosophy lock. The latter contributes to guarantees of progress forward with the possible expenditure of duplicate or excess work. Multitrack racing for initialization is correctly allowed on a fully substantiated theoretical basis:
- the race entry point (initialization code) is configured and protected globally (using the .NET loader), so that (several) riders (if any) enter the same initializer, which can never be considered
null . - several racing products (getters) are always logically identical, so it does not matter which rider (or a later non-racing caller) can pick up or even win any rider using the one they produced themselves;
- each installation swap is a single
IntPtr sized repository that is guaranteed to be atomic for any corresponding platform bit; - finally, and technically absolutely critical for perfect formal correctness, the work products of the "losers" are restored by the
GC and thus do not leak. In this type of race , the losers are every rider except the last finisher (since all other efforts become careless and totally rewritten with an identical result).
Although I believe that these points together fully protect the code written under any possible circumstances, if you are still suspicious or afraid of a general conclusion, you can always add an additional layer of armor protection:
var tmp = (Func<Ectx, Sctx>)dm.CreateDelegate(typeof(Func<Ectx, Sctx>)); Thread.MemoryBarrier(); __get = tmp; return tmp(arg);
This is just a paranoid version. As with the earlier condensed single layer, the .NET memory model ensures that there is exactly one store - and zero samples - at the __get location. (The full extended example at the top makes an additional sample of the main memory, but still sounds thanks to the second marker point). As I already mentioned, none of this should be necessary for correctness, but theoretically it can give a minimal performance bonus: after finishing the race earlier, an aggressive flash could, in an extremely rare case, prevent the caller from re-calling the dirty cache line unnecessarily (but again harmless).
Double thunking
The call to the final, ultrafast method is still thunked through the static extension methods shown earlier. This is because we also need to somehow introduce the entry points that actually exist at compile time for the compiler to bind and distribute metadata. Double-Thunk is a small price to pay for the overwhelming convenience of highly typed metadata and intellisense in the IDE for customized code that cannot actually be resolved before execution. Nevertheless, it works at least as fast as a statically compiled code, faster, which makes a bunch of reflection on every call, so we get the best of both worlds!