匿名方法、范围和序列化

发布于 2024-08-01 16:50:49 字数 653 浏览 6 评论 0原文

假设我有以下代码:

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

在这种情况下,对象和值的范围会发生什么情况? 由于 x 是值类型,因此它按值传递给 Bar,因此其范围不需要发生任何变化。 但 y 会怎样呢? y 的值需要保留下来,以便在实际计算 b 时返回。 是否所有 Foo 都保留下来以便稍后评估 y ? 我只能假设 Foo 没有被 GC 处理。

现在假设我们将 Bar 序列化到磁盘,然后再将其反序列化。 实际上已经连载了哪些内容? 它也序列化了 Foo 吗? 到底发生了什么魔力,使得在 Bar 反序列化后可以对 b 进行求值? 你能解释一下 IL 中发生了什么吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

謌踐踏愛綪 2024-08-08 16:50:49

当它反映要序列化的对象时尝试序列化时出现错误。

我的示例:

[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();


}
}

这是执行此操作的链接...稍微复杂一点: 序列化匿名委托

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

乖不如嘢 2024-08-08 16:50:49

我认为 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, () => y); 

可能被替换为

return new Bar(x,new CompilerGeneratedClassName(x,y).CompilerGeneratedMethodName);

因此,我认为该闭包不会导致对 Foo 对象的引用。 所以 Foo 对象可以被 GC 处理。 我可能是错的。 您可以做的一件事是编写一个小程序,对其进行编译,然后在 ILDASM 工具中检查生成的 IL。

I think x and y in Foo object will be captured as value types. So the closure created for that lambda expression should not keep a reference to the Foo object. So the compiler may create a class for that closure as:

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

and

return new Bar(x, () => y); 

may be replaced by

return new Bar(x,new CompilerGeneratedClassName(x,y).CompilerGeneratedMethodName);

So I don't think that that there will be reference to the Foo object as a result of this closure. So Foo object could be GCed. I could be wrong. One thing that you can do is to write a small program, compile it, and inspect the generated IL in ILDASM tool.

梦醒灬来后我 2024-08-08 16:50:49

创建一个快速测试项目来输出值,然后查看它们。 它应该回答问题,并且可能会让您在这个过程中学到一些额外的东西。 (这是大多数回答你问题的人所做的。)

Create a quick test project to output the values and then look at them. It should answer the questions, and probably cause you to learn something extra in the process. (This is what most of the people who will answer your question have done.)

朮生 2024-08-08 16:50:49

更新:查看实际发生的情况,而不必求助于 IL: 使用反射器来理解匿名方法和捕获的变量


当你使用时:

public Bar CreateBar()
{
    return new Bar(x, () => y);
}

你隐含着this.y; 因此,就委托而言,它包含对 Foo引用。 因此,Bar 的实例(通过委托)使整个 Foo 保持活动状态(不会被垃圾回收),直到 Bar 可用于收藏。

特别是,(在这种情况下)编译器不需要生成额外的类来处理捕获的变量; 唯一需要的是 Foo 实例,因此可以在 Foo 上生成一个方法。 如果委托涉及局部变量(this 除外),情况会更加复杂。

就序列化而言......好吧,我要说的第一件事是序列化委托是一个非常非常糟糕的主意。 但是,BinaryFormatter 遍历委托,并且您(理论上)最终可以得到一个序列化的 Bar、一个序列化的 Foo 和一个序列化委托来链接它们 - 但如果您将 Foo 标记为 [Serialized]

但我强调 - 这是一个坏主意。 我很少使用 BinaryFormatter(出于多种原因),但我看到使用它的人的一个常见问题是“为什么它尝试序列化(某种随机类型)”。 通常,答案是“您正在发布一个事件,并且它正在尝试序列化订阅者”,在这种情况下,最常见的修复方法是将事件的字段标记为 [NonSerialized]


而不是看IL; 研究此问题的另一种方法是在 .NET 1.0 模式下使用反射器(即不交换匿名方法); 然后你可以看到:

public Bar CreateBar()
{
    return new Bar(this.x, new Func<int>(this.<CreateBar>b__0));
}
[CompilerGenerated]
private int <CreateBar>b__0()
{
    return this.y;
}

如你所见; 传递给 Bar 的是当前实例 (this) 上隐藏方法(称为 b__0())的委托。 因此它传递给Bar的当前Foo的实例。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文