Let's say I have the following code:
public class Foo
{
private int x;
private int y;
public Bar CreateBar()
{
return new Bar(x, () => y);
}
}
[Serializable]
public class Bar
{
private int a;
private Func<int> b;
public Bar(int a, Func<int> b)
{
this.a = a;
this.b = b;
}
}
What happens with the scope of the objects and values in this scenario? Since x is a value type, it is passed to Bar by value, and therefore, nothing needs to happen to its scope. But what happens to y? The value for y needs to stick around to be returned when b is actually evaluated. Is all of Foo kept around to evaluate y at a later time? I can only assume that Foo is not GC'ed.
Now let's say that we serialize Bar to disk, then deserialize it later. What has actually been serialized? Did it serialze Foo as well? What magic has gone on so that b can be evaluated after Bar has been deserialized? Can you explain what is happening in the IL?
Update: to see what is actually happening without having to resort to IL: Using reflector to understand anonymous methods and captured variables
When you use:
public Bar CreateBar()
{
return new Bar(x, () => y);
}
You are implicitly meaning this.y
; so in terms of the delegate, it is the reference to Foo
that is included. As such, the instance of Bar
(via the delegate) keeps the entire Foo
alive (not garbage-collected) until the Bar
is available for collection.
In particular, there is no need (in this case) for the compiler to generate an additional class to handle captured variables; the only thing required is the Foo
instance, so a method can be generated on Foo
. This would be be more complex if the delegate involved local variables (other than this
).
In terms of serialization... well, the first thing I'd say is that serializing delegates is a very very bad idea. However, BinaryFormatter
will walk delegates, and you can (in theory) end up with a serialized Bar
, a serialized Foo
, and a serialized delegate to link them - but only if you mark Foo
as [Serializable]
.
But I stress - this is a bad idea. I rarely use BinaryFormatter
(for a variety of reasons), but a common question I see by people using it is "why is it trying to serialize (some random type)". Usually, the answer is "you are publishing an event, and it is trying to serialize the subscriber", in which case the most common fix would be to mark the event's field as [NonSerialized]
.
Rather than looking at IL; another way to investigate this is to use reflector in .NET 1.0 mode (i.e. without it swapping in anonymous methods); then you can see:
public Bar CreateBar()
{
return new Bar(this.x, new Func<int>(this.<CreateBar>b__0));
}
[CompilerGenerated]
private int <CreateBar>b__0()
{
return this.y;
}
As you can see; the thing passed to Bar
is a delegate to a hidden method (called <CreateBar>b__0()
) on the current instance (this
). So it is the instance to the current Foo
that is passed to the Bar
.
I got error when trying to serialize when it was reflecting the object to serialize.
my example:
[Serializable]
public class SerializeTest
{
//public SerializeTest(int a, Func<int> b)
//{
// this.a = a;
// this.b = b;
//}
public SerializeTest()
{
}
public int A
{
get
{
return a;
}
set
{
a = value;
}
}
public Func<int> B
{
get
{
return b;
}
set
{
b = value;
}
}
#region properties
private int a;
private Func<int> b;
#endregion
//serialize itself
public string Serialize()
{
MemoryStream memoryStream = new MemoryStream();
XmlSerializer xs = new XmlSerializer(typeof(SerializeTest));
using (StreamWriter xmlTextWriter = new StreamWriter(memoryStream))
{
xs.Serialize(xmlTextWriter, this);
xmlTextWriter.Flush();
//xmlTextWriter.Close();
memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
memoryStream.Seek(0, SeekOrigin.Begin);
StreamReader reader = new StreamReader(memoryStream);
return reader.ReadToEnd();
}
}
//deserialize into itself
public void Deserialize(string xmlString)
{
String XmlizedString = null;
using (MemoryStream memoryStream = new MemoryStream())
{
using (StreamWriter w = new StreamWriter(memoryStream))
{
w.Write(xmlString);
w.Flush();
XmlSerializer xs = new XmlSerializer(typeof(SerializeTest));
memoryStream.Seek(0, SeekOrigin.Begin);
XmlReader reader = XmlReader.Create(memoryStream);
SerializeTest currentConfig = (SerializeTest)xs.Deserialize(reader);
this.a = currentConfig.a;
this.b = currentConfig.b;
w.Close();
}
}
}
}
class Program
{
static void Main(string[] args)
{
SerializeTest test = new SerializeTest() { A = 5, B = ()=>67};
string serializedString = test.Serialize();
}
}
Here is a link to doing it...little more complex: Serializing Anon Delegates
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With