如何BinaryFormatter.Deserialize创建新的对象? [英] How does BinaryFormatter.Deserialize create new objects?

查看:630
本文介绍了如何BinaryFormatter.Deserialize创建新的对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

的BinaryFormatter 反序列化流为对象,这似乎不调用构造函数来创建新的对象。

When BinaryFormatter deserializes a stream into objects, it appears to create new objects without calling constructors.

如何它是这样做呢?为什么?是否还有其他的.NET,这是否

How is it doing this? And why? Is there anything else in .NET that does this?

下面是一个演示:

[Serializable]
public class Car
{
    public static int constructionCount = 0;

    public Car()
    {
        constructionCount++;
    }
}

public class Test
{
    public static void Main(string[] args)
    {
        // Construct a car
        Car car1 = new Car();

        // Serialize and then deserialize to create a second, identical car
        MemoryStream stream = new MemoryStream();
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, car1);
        stream.Seek(0, SeekOrigin.Begin);
        Car car2 = (Car)formatter.Deserialize(stream);

        // Wait, what happened?
        Console.WriteLine("Cars constructed: " + Car.constructionCount);
        if (car2 != null && car2 != car1)
        {
            Console.WriteLine("But there are actually two.");
        }
    }
}



输出:

Output:

汽车构造:1。

但是,实际上有两个

推荐答案

有两件事情调用构造函数(或者至少应该做的)。

There are two things calling a constructor does (or at least should do).

一是预留了一定数量的内存为对象,做所有的家务必要为它是一个对象的.NET世界其他地方(注意一定handwaving的在这种解释)

One is to set aside a certain amount of memory for the object and does all the housekeeping necessary for it to be an object to the rest of the .NET world (note certain amount of handwaving in this explanation).

另外就是把对象变成一个有效的初始状态,也许是基于参数 - 这就是在构造函数中的实际代码就行了。

The other is to put the object into a valid initial state, perhaps based on parameters - this is what the actual code in the constructor will do.

Deserialisation做同样的事情,作为第一步,通过调用 FormatterServices.GetUninitializedObject ,然后做大致相同作为第二步骤通过对字段设置值事等同于那些序列期间记录(这可能需要deserialising其它目的是所述值)。

Deserialisation does much the same thing as the first step by calling FormatterServices.GetUninitializedObject, and then does much the same thing as the second step by setting the values for fields to be equivalent to those that were recorded during serialisation (which may require deserialising other objects to be said values).

现在,这deserialisation是把物体进入状态不得以任何构造对应于可能的。在最好的情况将是浪费(由构造函数设置的所有值将被覆盖),并在更糟可能是危险的(构造有一定的副作用)。它也可能仅仅是不可能的(唯一的构造是一个带有参数 - 系列化无法知道使用什么参数的方式)。

Now, the state that deserialisation is putting the object into may not correspond to that possible by any constructor. At best it will be wasteful (all values set by the constructor will be overwritten) and at worse it could be dangerous (constructor has some side-effect). It could also just be impossible (only constructor is one that takes parameters - serialisation has no way of knowing what arguments to use).

您可以看看它作为一个特殊的构造函数的排序仅deserialisation使用(OO纯粹主义者 - 也应该 - 不寒而栗在不建立一个构造函数的想法,我只意味着这是一个比喻,如果你知道C ++想到的方式覆盖工作尽可能内存的推移而你一个更好的比喻,虽然仍只是打个比方)。

You could look at it as a special sort of constructor only used by deserialisation (OO purists will - and should - shudder at the idea of a constructor that doesn't construct, I mean this as an analogy only, if you know C++ think of the way overriding new works as far as memory goes and you've an even better analogy, though still just an analogy).

现在,这样可以是一个问题,在某些情况下 - 也许我们只能通过构造函数来设置只读字段,或者也许我们有副作用,我们的需要发生。

Now, this can be a problem in some cases - maybe we have readonly fields that can only be set by a constructor, or maybe we have side-effects that we want to happen.

一个解决方案,这两个是覆盖与 ISerializable的。这将连载基于对 ISerializable的一个电话。 GetObjectData使用 ,然后调用与的SerializationInfo 的StreamingContext 字段特定构造deserialise(据说构造,甚至可以是私有的 - 这意味着大多数其他代码甚至不会看到它)。因此,如果我们能deserialise 只读字段,有我们想要的任何副作用(我们也可以做各式各样的东西来控制究竟什么是序列化以及如何)。

A solution to both is to override serialisation behaviour with ISerializable. This will serialise based on a call to ISerializable.GetObjectData and then call a particular constructor with SerializationInfo and StreamingContext fields to deserialise (said constructor can even be private - meaning most other code won't even see it). Hence if we can deserialise readonly fields and have any side-effects we want (we can also do all manner of things to control just what is serialised and how).

如果我们只关心确保一定的副作用发生在deserialisation会发生在建筑,我们可以实现 IDeserializationCallback 和我们将有 IDeserializationCallback.OnDeserialization 调用时deserialisation完成。

If we just care about ensuring some side-effect happens on deserialisation that would happen on construction, we can implement IDeserializationCallback and we will have IDeserializationCallback.OnDeserialization called when deserialisation is complete.

至于该做同样的事情,因为这其他的东西,也有.NET其他形式的系列化但是这是我所知道的。它可以调用 FormatterServices.GetUninitializedObject 自己,但除非,你有一个强大的保证,后面的代码就会把制作成有效状态的对象的情况下(即精确的排序情况下,你是deserialising从连载同一类对象的产生的数据对象时)做这样充满并产生一个很艰难的诊断错误的好办法。

As for other things that do the same thing as this, there are other forms of serialisation in .NET but that's all I know of. It is possible to call FormatterServices.GetUninitializedObject yourself but barring a case where you have a strong guarantee that subsequent code will put the object produced into a valid state (i.e. precisely the sort of situation you are in when deserialising an object from data produced by serialising the same sort of object) doing such is fraught and a good way to produce a really hard to diagnose bug.

这篇关于如何BinaryFormatter.Deserialize创建新的对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆