I usually call these variables "free variables of the closure" but it seems that the most accurate name is upvalues. Well, anyway, the question is, is it possible to have access to these values?
- I think it's not possible in JavaScript.
- I think to remember that it was possible in Python (I think somewhere in the function object or the code object there was a free_vars property).
- I've tested that it is possible in C#.
The way I've found to get access to these variables fully depends on how the C# compiler implements closures, I guess this is not part of any specification. So, it's just something that anyone can check with Reflector but could change in future versions.
Let's say we have a method Formatter that formats some strings and we'd like to add some state info to the method, e.g. how many times it has been called. Well, it's easy, we just enclose the method into a closure:
1: Func<Func<string, string, string>> counterClosureGenerator = delegate()
2: {
3: int counter = 0;
4: return delegate(string st1, string st2)
5: {
6: counter++;
7: return Formatter(st1, st2);
8: };
9: };
10:
11: Func<string, string, string> enclosedFormatter = counterClosureGenerator();
where enclosedFormatter is the closure and counter stores the number of times it's been called.
Unfortunately we can't do something like: enclosedFormatter.FreeVars["counter"] or anything like that... so what?
Well, if we know a bit about how closures are implemented, we should be aware that the anonymous method that is the base for the closure is created as a normal method inside a normal class (with some esoteric name like <>c__DisplayClass3), that will contain (as public fields) those free variables used by the closure.
With all this in mind, and with a bit of Reflection (Introspection) magic, getting access to one of those free variables is as easy as this:
1: class ClosureHelper
2: {
3: public static object GetValue(Delegate deleg, string varName)
4: {
5: FieldInfo field = deleg.Target.GetType().GetField(varName);
6: return field.GetValue(deleg.Target);
7: }
8:
9: public static void SetValue(Delegate deleg, string varName, object value)
10: {
11: FieldInfo field = deleg.Target.GetType().GetField(varName);
12: field.SetValue(deleg.Target, value);
13: }
14: }
OK, sure this is not much useful, but it was fun to write about it. You can download the source here.
No comments:
Post a Comment