With Don Box doing all manner of crazyness with anonymous delegates recently, I decided to do some experimenting of my own. While doing so, I ran into an interesting problem. I was toying with a robust action scheduling mechanism that serializes actions to disk to make sure that they won't be lost in the case where the host process is stopped for some reason.
I was using the generic Action delegate to represent the actions and using anonymous delegates to inject things like action rescheduling. I was pleased to see that basic anonymous delegates were serialized without issue. But, when I started to use the closure features of anonymous delegates, it stopped working. I was dissapointed, especially when I figured out why.
When you create an anonymous delegate without using outer variables, it is hoisted into the enclosing class as a private method with a compiler-generated name (that appears to be generated based on a combination of the signature and some randomness). For example:
[Serializable]public class Example { void Test() { Action action = delegate(DateTime scheduledTime) { Console.WriteLine("blah"); }; }}
This actually turns into something like:
[Serializable]public class Example { void Test() { Action action = new Action(CompilerGeneratedName); } void CompilerGeneratedName(DateTime dateTime) { Console.WriteLine("blah"); }}
Actually, it does some niftyness to cache the delegate, but that's not important for this example. As you can see, serialization isn't a problem here. However, if you use an outer variable within the method, the compiler hoists both the method and the variable into a nested class so it's available later when the delegate actually runs. For example:
[Serializable]public class Example { void Test() { string foo = "blah"; Action action = delegate(DateTime scheduledTime) { Console.WriteLine(foo); }; }}
This turns into something like:
[Serializable]public class Example { void Test() { CompilerGeneratedClassName something = new CompilerGeneratedClassName(); something.foo = "blah"; Action action = new Action(something.CompilerGeneratedName); } private class CompilerGeneratedClassName { public string foo; void CompilerGeneratedName(DateTime dateTime) { Console.WriteLine(foo); }}
Pretty cool, eh? Yeah, so what's the problem? Well, the nested class is not marked with SerializableAttribute, so it can't be serialized, which makes the delegate serialization fail. This seems like a big problem to me. There may be a legitimate reason for this, but I don't see it. I think the compiler ought to decorate the nested class if the enclosing class is also decorated.
There may be a way around this, but I haven't found it yet. Any suggestions?
[Update] If you think this is a problem too, then vote for it.
Remember Me
Page rendered at Sunday, October 12, 2008 12:42:52 AM (Pacific Standard Time, UTC-08:00)
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.