[英]How do I deserialize a collection of references to a struct equivalent?
NOTE: This question changed a little as I learned more about the problem, so please read it in its entirety. 注意:随着我对这个问题的了解更多,这个问题有所改变,因此请完整阅读。 I've decided to leave it in its original form as it better describes how the problem was discovered and ultimately resolved. 我决定保留它的原始形式,因为它可以更好地描述问题是如何发现和最终解决的。
Way back in the darkest depths of our project's history when we didn't really understand C# or the CLR as well as we might've, we created a type, let's call it MyType
. 当我们对C#或CLR不太了解时,回到了项目历史的最深处,我们创建了一个类型,我们将其称为MyType
。 We created this type as a class
, a reference type. 我们将此类型创建为class
(引用类型)。
However, it became apparent that MyType
should be a struct
, a value type so we made some changes to make it so and all was well until one day, we tried to deserialize some data that contained a collection of MyType
values. 但是,很明显MyType
应该是一个struct
,一个值类型,因此我们做了一些更改以使它如此,直到一天之内,我们都尝试对包含MyType
值集合的某些数据进行反序列化。 Well, not really, for when it was a reference type, that collection was a collection of references. 好吧,不是真的,因为当它是引用类型时,该集合就是引用的集合。 Now when it deserializes, the collection deserializes fine, using the default constructor of MyType
, then later when the actual references deserialize, they are orphaned, leaving us with a collection of empty values. 现在,当它反序列化时,该集合可以使用MyType
的默认构造函数进行反序列化,然后在实际引用反序列化之后,它们被孤立,从而为我们留下了一个空值集合。
So, we thought, "let's map the type to a reference type, MyTypeRef
on load so that the references properly resolve, then convert back to our real type for use during execution time and reserialization". 因此,我们认为,“让我们将类型映射到引用类型, MyTypeRef
在加载时将MyTypeRef
映射为引用,以便引用能够正确解析,然后转换回我们的真实类型以在执行时间和重新序列化期间使用”。 So we did (using our own binder), but alas, it didn't work because now we get an error that tells us MyTypeRef[]
can't be converted to MyType[]
even if we have an implicit conversion between MyTypeRef
and MyType
. 所以我们做到了(使用我们自己的活页夹),但是,它没有用,因为现在我们收到一个错误,告诉我们即使在MyTypeRef
和MyType
之间进行隐式转换,也无法将MyTypeRef[]
转换为MyType[]
。
So, we're stuck. 因此,我们陷入困境。 How do we get a collection serialized as a collection of reference type MyType
to deserialize as a collection of value type MyType
? 如何获得序列化为引用类型MyType
的集合的序列集,反序列化为值类型MyType
的集合的集合?
Update 更新
Some investigation (see comments and code below) has shown that it is the immutable nature of the new MyType
and its used of ISerializable
for serialization that has caused the real issue. 一些调查(请参阅下面的注释和代码)表明,新的MyType
的不可变性质及其对序列化使用ISerializable
导致了真正的问题。 I still don't see why that should be the case, but if I use private
set accessors instead of ISerializable
, the new MyType
will load the old (note that the ISerializable
interface is called if I use it but the collection only even contains default values). 我仍然不知道为什么会这样,但是如果我使用private
集访问器而不是ISerializable
,则新的MyType
将加载旧的(请注意,如果我使用ISerializable
接口,则该接口将被调用,但是该集合甚至只包含默认值)值)。
Some code 一些代码
// Use a List<T> in a class that also implements ISerializable and
// save an instance of that class with a BinaryFormatter (code omitted)
// Save with this one.
[Serializable]
public class MyType
{
private string test;
public string Test
{
get { return this.test; }
set { this.test = value; }
}
public MyType()
{
}
}
// Load with this one.
[Serializable]
public class MyType : ISerializable
{
private string test;
public string Test
{
get { return this.test; }
set { this.test = value; }
}
public MyType()
{
}
public MyType(SerializationInfo info, StreamingContext context)
{
info.AddValue("test", this.test);
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
this.test = info.GetString("test");
}
}
Note that when loading, the elements are all nulls. 请注意,加载时,所有元素均为空。 Remove ISerializable
from the second definition and load and all works. 从第二个定义中删除ISerializable
,然后加载并完成所有工作。 Change class
to struct
on the load code and the same behaviour is visible. 更改class
以对加载代码进行struct
,并且可以看到相同的行为。 It is as though the collection will only deserialize successfully using the set accessors. 好像集合只能使用set访问器成功反序列化。
Update Two 更新二
So, I found the problem (see my answer below) but I doubt anyone would've known the answer from reading my question. 因此,我发现了问题(请参阅下面的答案),但我怀疑有人会从阅读我的问题时知道答案。 I missed out an important detail that I didn't even realise was important at the time. 我错过了一个重要细节,而我当时甚至没有意识到它很重要。 My sincerest apologies to those who tried to help. 我对那些试图帮助的人表示最诚挚的歉意。
The collection that is loaded containing MyType
is immediately copied to another collection during GetObjectData
. 加载的包含MyType
集合将在GetObjectData
期间立即复制到另一个集合。 The answer below explains why this turns out to be important. 下面的答案解释了为什么这很重要。 Here is some additional sample code to that given above that should provide a complete example: 这是上面给出的一些示例代码,应提供一个完整的示例:
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
this.myTypeCollection = new List<MyType>();
var loadedCollection = (List<MyType>)info.GetValue(
"myTypeCollection",
typeof(List<MyType>));
this.myTypeCollection.AddRange(loadedCollection);
}
Not sure to understand, but if I do this on save: 不确定是否了解,但是如果我在保存时这样做:
[Serializable]
public class MyTypeColl
{
public MyTypeColl()
{
}
public List<MyType> Coll { get; set; }
}
[Serializable]
public class MyType
{
private string test;
public string Test
{
get { return this.test; }
set { this.test = value; }
}
public MyType()
{
}
}
// save code
MyTypeColl coll = new MyTypeColl();
coll.Coll = new List<MyType>();
coll.Coll.Add(new MyType{Test = "MyTest"});
BinaryFormatter bf = new BinaryFormatter();
using (FileStream stream = new FileStream("test.bin", FileMode.OpenOrCreate))
{
bf.Serialize(stream, coll);
}
And this on load: 而这个负载:
[Serializable]
public struct MyType
{
private string test;
public string Test
{
get { return this.test; }
set { this.test = value; }
}
}
// load code
BinaryFormatter bf = new BinaryFormatter();
using (FileStream stream = new FileStream("test.bin", FileMode.Open))
{
MyTypeColl coll = (MyTypeColl)bf.Deserialize(stream);
Console.WriteLine(coll.Coll[0].Test);
}
It displays "MyTest" successfuly. 它成功显示“ MyTest”。 So what am I missing? 那我想念什么呢?
I feel somewhat foolish now but I discovered the issue. 我现在觉得有些愚蠢,但发现了问题。 I have updated the question with additional sample code. 我已经用其他示例代码更新了问题。
Here is the basics of what the code was doing in the GetObjectData
method: 以下是代码在GetObjectData
方法中执行的操作的基础知识:
Here is the sequence that occurred at execution: 这是执行时发生的顺序:
Note that it is only after point 3 that the entire collection is deserialized but I have already done my copy, hence having nothing. 请注意,只有在第3点之后才对整个集合进行反序列化,但是我已经完成了复制,因此一无所获。
The fix was to use the OnDeserialized
attribute to specify a method to call once the entire object was deserialized. 解决方法是使用OnDeserialized
属性指定在反序列化整个对象后要调用的方法。 Then, in GetObjectData
I save away a reference to the collection that is loaded, but copy its contents in the OnDeserialized
method once it is fully deserialized. 然后,在GetObjectData
我保存了对已加载的集合的引用,但是在完全反序列化之后,将其内容复制到OnDeserialized
方法中。
Side note 边注
In fact, to keep the deserialization code all in GetObjectData
, I saved away a delegate to perform the copy and I call that delegate later - that way it is clear in GetObjectData
exactly what will happen to finish deserialization. 实际上,为了将反序列化代码全部保留在GetObjectData
,我保存了一个委托来执行复制,然后稍后调用该委托-这样,在GetObjectData
就可以清楚地知道完成反序列化的过程。
Code 码
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
var loadedCollection = (List<MyType>)info.GetValue(
"myTypeCollection",
typeof(List<MyType>));
this.myTypeCollectionLoader = () =>
{
this.myTypeCollection.AddRange(loadedCollection);
};
}
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
this.myTypeCollectionLoader();
this.myTypeCollectionLoader = null;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.