Created
June 26, 2011 13:30
-
-
Save JohannesRudolph/1047616 to your computer and use it in GitHub Desktop.
Caller-dependent method level local statics implementation in C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.Linq; | |
using System.Linq.Expressions; | |
using System.Reflection; | |
namespace MethodStaticLocals | |
{ | |
class ExpensiveObject | |
{ | |
public ExpensiveObject() | |
{ | |
Console.WriteLine( "Creating Expensive object" ); | |
} | |
}; | |
class MainClass | |
{ | |
public static void Main( string[] args ) | |
{ | |
Expression<Action> call = () => Func( "hello" ); | |
Invoke( call ); | |
Invoke( call ); | |
} | |
// caches handles for expresisons, as they are expensive to find. | |
static Dictionary<Expression, RuntimeMethodHandle> handleCache = new Dictionary<Expression, RuntimeMethodHandle>(); | |
// static locals are managed per method handle | |
static Dictionary<RuntimeMethodHandle, Dictionary<string, object>> staticLocals = new Dictionary<RuntimeMethodHandle, Dictionary<string, object>>(); | |
// redirects are individual for each expression tree | |
static Dictionary<Expression, Delegate> redirects = new Dictionary<Expression, Delegate>(); | |
static void Invoke( Expression<Action> call ) | |
{ | |
if (call.Parameters != null && call.Parameters.Any()) | |
throw new InvalidOperationException(); | |
if (call.Body.NodeType != ExpressionType.Call) | |
throw new InvalidOperationException(); | |
Delegate redirectedInvocation = SetupRedirectedInvocation( call ); | |
redirectedInvocation.DynamicInvoke(); | |
} | |
private static Delegate SetupRedirectedInvocation( Expression<Action> call ) | |
{ | |
Delegate redirectedInvocation; | |
if (!redirects.TryGetValue( call, out redirectedInvocation )) | |
{ | |
RuntimeMethodHandle caller = SetupCaller( call ); | |
Console.WriteLine( "Creating redirect for {0}", caller.Value ); | |
MethodCallExpression callExpression = (MethodCallExpression)call.Body; | |
// add staticLocals dictionary as argument | |
var arguments = callExpression.Arguments.ToList(); | |
arguments.Add( Expression.Constant( staticLocals[caller] ) ); | |
// todo: dynamically find redirect | |
var redirect = MethodOf( () => Func( default( string ), default( Dictionary<string, object> ) ) ); | |
LambdaExpression redirectedExpression = Expression.Lambda( Expression.Call( callExpression.Object, redirect, arguments ), new ParameterExpression[0] ); | |
redirectedInvocation = redirectedExpression.Compile(); | |
redirects.Add( call, redirectedInvocation ); | |
} | |
return redirectedInvocation; | |
} | |
private static RuntimeMethodHandle SetupCaller( Expression<Action> call ) | |
{ | |
RuntimeMethodHandle caller; | |
if (!handleCache.TryGetValue( call, out caller )) | |
{ | |
caller = new StackFrame( 1 ).GetMethod().MethodHandle; | |
handleCache.Add( call, caller ); | |
staticLocals.Add( caller, new Dictionary<string, object>() ); | |
} | |
return caller; | |
} | |
public static MethodInfo MethodOf( Expression<Action> expression ) | |
{ | |
MethodCallExpression body = (MethodCallExpression)expression.Body; | |
return body.Method; | |
} | |
[Obsolete( "do not call directly" )] | |
public static void Func( string arg ) | |
{ | |
} | |
private static void Func( string arg, Dictionary<string, object> staticLocals ) | |
{ | |
if (!staticLocals.ContainsKey( "expensive")) | |
{ | |
staticLocals.Add( "expensive", new ExpensiveObject() ); | |
} | |
ExpensiveObject obj = (ExpensiveObject)staticLocals["expensive"]; | |
Console.WriteLine( "Func invoked: arg: {0}; expensive: {1}", arg, obj ); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment