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;
}