C# 匿名方法、作用域和序列化
假设我有以下代码:C# 匿名方法、作用域和序列化,c#,delegates,scope,anonymous-methods,C#,Delegates,Scope,Anonymous Methods,假设我有以下代码: 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(i
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;
}
}
公共类Foo
{
私人INTX;
私营企业;
公共工具栏CreateBar()
{
返回新条(x,()=>y);
}
}
[可序列化]
公共类酒吧
{
私人INTA;
私人职能b;
公共酒吧(内部a、职能b)
{
这个a=a;
这个.b=b;
}
}
在这个场景中,对象和值的范围会发生什么变化?由于x是一种值类型,它是按值传递给Bar的,因此,它的作用域不需要发生任何变化。但是y怎么了?当实际计算b时,需要返回y的值。所有的Foo都留着在以后评估y吗?我只能假设Foo不是GC'ed
现在让我们假设我们将Bar序列化到磁盘,然后稍后将其反序列化。什么已经被序列化了?它也把Foo连载了吗?是什么魔力使b可以在Bar被反序列化后进行计算?你能解释一下IL中发生了什么吗?创建一个快速测试项目来输出值,然后查看它们。它应该能回答问题,并且可能会让你在这个过程中学到一些额外的东西。(这是大多数回答您问题的人都做过的。)我在尝试序列化时出错,因为它反映了要序列化的对象 我的例子是:
[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();
}
}
[可序列化]
公共类序列化测试
{
//公共测试(int a,Func b)
//{
//这个a=a;
//这个.b=b;
//}
公共测试()
{
}
公共INTA
{
得到
{
返回a;
}
设置
{
a=数值;
}
}
公共职能B
{
得到
{
返回b;
}
设置
{
b=数值;
}
}
#区域属性
私人INTA;
私人职能b;
#端区
//序列化自身
公共字符串序列化()
{
MemoryStream MemoryStream=新的MemoryStream();
XmlSerializer xs=新的XmlSerializer(typeof(SerializeTest));
使用(StreamWriter xmlTextWriter=新StreamWriter(memoryStream))
{
序列化(xmlTextWriter,this);
xmlTextWriter.Flush();
//xmlTextWriter.Close();
memoryStream=(memoryStream)xmlTextWriter.BaseStream;
memoryStream.Seek(0,SeekOrigin.Begin);
StreamReader=新的StreamReader(memoryStream);
返回reader.ReadToEnd();
}
}
//反序列化为自身
public void反序列化(字符串xmlString)
{
字符串XmlizedString=null;
使用(MemoryStream MemoryStream=new MemoryStream())
{
使用(StreamWriter w=新StreamWriter(memoryStream))
{
w、 写入(xmlString);
w、 冲洗();
XmlSerializer xs=新的XmlSerializer(typeof(SerializeTest));
memoryStream.Seek(0,SeekOrigin.Begin);
XmlReader=XmlReader.Create(memoryStream);
SerializeTest currentConfig=(SerializeTest)xs.Deserialize(读取器);
this.a=currentConfig.a;
this.b=currentConfig.b;
w、 Close();
}
}
}
}
班级计划
{
静态void Main(字符串[]参数)
{
SerializeTest=newserializetest(){A=5,B=()=>67};
string serializedString=test.Serialize();
}
}
这里有一个链接,可以实现这一点……稍微复杂一点:我认为Foo对象中的x和y将被捕获为值类型。因此,为该lambda表达式创建的闭包不应保留对Foo对象的引用。因此,编译器可以为该闭包创建一个类,如下所示:
internal class CompilerGeneratedClassName
{
private int x;
private int y;
public CompilerGeneratedClassName(int x, int y)
{
this.x = x;
this.y = y;
}
public int CompilerGeneratedMethodName()
{
return this.y;
}
}
及
可替换为
return new Bar(x,new CompilerGeneratedClassName(x,y).CompilerGeneratedMethodName);
所以我不认为这个闭包会导致对Foo对象的引用。所以Foo对象可以被GCed。我可能错了。您可以做的一件事是编写一个小程序,编译它,并在ILDASM工具中检查生成的IL。更新:要查看实际发生的情况,而不必求助于IL:
使用时:
public Bar CreateBar()
{
return new Bar(x, () => y);
}
您隐含的意思是this.y
;因此,就委托而言,包括对Foo
的引用。因此,Bar
的实例(通过委托)保持整个Foo
处于活动状态(不进行垃圾收集),直到Bar
可用于收集
特别是,编译器不需要(在本例中)生成额外的类来处理捕获的变量;唯一需要的是Foo
实例,因此可以在Foo
上生成方法。如果委托涉及局部变量(而不是This
),这将更加复杂
在序列化方面。。。首先我要说的是,序列化委托是一个非常糟糕的主意。但是,BinaryFormatter
将遍历代理,并且(理论上)最终可以得到一个序列化的条
、一个序列化的Foo
,以及一个序列化的代理来链接它们-但是只有在将Foo
标记为[Serializable]
时才可以
但我强调,这是一个坏主意。我很少使用BinaryFormatter
(出于各种原因),但使用它的人看到的一个常见问题是“为什么它试图序列化(某个随机类型)”。通常,答案是“您正在发布一个事件,它正在尝试序列化订阅服务器”,在wh中
public Bar CreateBar()
{
return new Bar(x, () => y);
}
public Bar CreateBar()
{
return new Bar(this.x, new Func<int>(this.<CreateBar>b__0));
}
[CompilerGenerated]
private int <CreateBar>b__0()
{
return this.y;
}