C# 在C中使用BinaryFormatter序列化对同一对象的引用#
我正在使用C# 在C中使用BinaryFormatter序列化对同一对象的引用#,c#,serialization,C#,Serialization,我正在使用BinaryFormatter序列化一个对象图,当对同一对象有多个引用时,我希望在反序列化后保留该引用,即反序列化数据中的引用仍应引用同一对象 这似乎适用于属于同一反序列化()调用的所有对象,但不适用于同一BinaryFormatter实例上的单独调用。有没有办法对其进行不同的配置,以便在多个调用中正确保留引用 请参见以下示例,其中ReferenceEquals()与来自不同反序列化()调用的对象一起返回false using System; using System.Diagnost
BinaryFormatter
序列化一个对象图,当对同一对象有多个引用时,我希望在反序列化后保留该引用,即反序列化数据中的引用仍应引用同一对象
这似乎适用于属于同一反序列化()调用的所有对象,但不适用于同一BinaryFormatter
实例上的单独调用。有没有办法对其进行不同的配置,以便在多个调用中正确保留引用
请参见以下示例,其中ReferenceEquals()
与来自不同反序列化()
调用的对象一起返回false
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace SerializeTest
{
[Serializable]
class Data
{
public int Value { get; set; }
}
class Program
{
static void Main(string[] args)
{
var data = new Data { Value = 1234 };
using (var fs = new FileStream("serialized.dat", FileMode.Create, FileAccess.Write))
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(fs, (data, data));
formatter.Serialize(fs, data);
}
using (var fs = new FileStream("serialized.dat", FileMode.Open, FileAccess.Read))
{
BinaryFormatter formatter = new BinaryFormatter();
var (data1, data2) = ((Data, Data))formatter.Deserialize(fs);
var data3 = (Data)formatter.Deserialize(fs);
// object references from separate calls are not the same?
// accordings to docs, same Formatter and ObjectIdGenerator should lead to the same object being deserialized.
Debug.Assert(ReferenceEquals(data1, data2), "objects from same call");
Debug.Assert(ReferenceEquals(data1, data3), "objects from different calls");
}
}
}
}
表面上看,这是不可能的,因为对象引用只对每个反序列化调用有意义。在内部,每个调用都会创建一个二进制文件来保存引用
如果你只想知道它们是否相等,你可以实现平等
[Serializable]
class Data
{
public int Value { get; set; }
public override bool Equals(object obj) =>
(obj is Data data) ? data.Value == Value : false;
public override int GetHashCode() => Value.GetHashCode();
}
这意味着:
Debug.Assert(Equals(data1, data2), "objects from same call");
Debug.Assert(Equals(data1, data3), "objects from different calls");
如果您绝对需要引用相等,可以通过实现,并记录结果,尝试在多个调用之间共享状态
更新:
好吧,如果让你白费力气去寻找一个可能根本不可行的解决方案,我会很难过。所以我自己试过:
让我们尝试添加一个类型,该类型通过将每个实例映射到一个唯一的id来包装引用等式,该id可以是GetHashCode
本身
[Serializable]
public class Ref<T>
{
public readonly T Value;
public readonly int Id;
public Ref(T value) { Value = value; Id = value.GetHashCode(); }
protected class RefSurrogate : ISerializationSurrogate
{
Dictionary<int, Ref<T>> Instances = new Dictionary<int, Ref<T>>();
public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
{
var refs = (Ref<T>)obj;
info.AddValue(nameof(Id), refs.Id);
if (!Instances.ContainsKey(refs.Id))
{
Instances.Add(refs.Id, refs);
info.AddValue(nameof(Value), refs.Value);
}
}
public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
{
var id = (int)info.GetInt32(nameof(Id));
if (Instances.TryGetValue(id, out var refs))
return refs;
else
return Instances[id] = new Ref<T>((T)info.GetValue(nameof(Value), typeof(T)));
}
}
public static SurrogateSelector AddTo(SurrogateSelector ss)
{
ss.AddSurrogate(typeof(Ref<T>), new StreamingContext(StreamingContextStates.All), new RefSurrogate());
return ss;
}
}
[可序列化]
公共类参考
{
公共只读T值;
公共只读int-Id;
公共引用(T值){value=value;Id=value.GetHashCode();}
受保护的类引用代理:ISerializationSubrogate
{
字典实例=新字典();
public void GetObjectData(对象对象对象、序列化信息、StreamingContext上下文)
{
var refs=(Ref)obj;
info.AddValue(name of(Id),refs.Id);
如果(!Instances.ContainsKey(refs.Id))
{
添加(refs.Id,refs);
信息添加值(名称(值)、参考值);
}
}
公共对象SetObjectData(对象对象对象、序列化信息、StreamingContext上下文、iUrrogateSelector选择器)
{
var id=(int)info.GetInt32(nameof(id));
if(Instances.TryGetValue(id,out var refs))
返回参考文献;
其他的
返回实例[id]=newref((T)info.GetValue(nameof(Value),typeof(T));
}
}
公共静态代理选择程序AddTo(代理选择程序ss)
{
AddSurrogate(typeof(Ref)、newstreamingContext(streamingContextState.All)、new RefSurrogate());
返回ss;
}
}
以及测试程序:
[Serializable]
class Data
{
public int Value { get; set; }
}
class Program
{
static void Main(string[] args)
{
var data = new Ref<Data>(new Data { Value = 1234 });
BinaryFormatter formatter = new BinaryFormatter();
using (var fs = new FileStream("serialized.dat", FileMode.Create, FileAccess.Write))
{
formatter.Serialize(fs, (data, data));
formatter.Serialize(fs, data);
}
using (var fs = new FileStream("serialized.dat", FileMode.Open, FileAccess.Read))
{
// Create a SurrogateSelector.
formatter.SurrogateSelector = Ref<Data>.AddTo(new SurrogateSelector());
var (data1, data2) = ((Ref<Data>, Ref<Data>))formatter.Deserialize(fs);
var data3 = (Ref<Data>)formatter.Deserialize(fs);
// object references from separate calls are not the same?
// accordings to docs, same Formatter and ObjectIdGenerator should lead to the same object being deserialized.
Debug.Assert(ReferenceEquals(data1.Value, data2.Value), "objects from same call");
Debug.Assert(ReferenceEquals(data1.Value, data3.Value), "objects from different calls");
}
}
}
[可序列化]
类数据
{
公共int值{get;set;}
}
班级计划
{
静态void Main(字符串[]参数)
{
var数据=新引用(新数据{Value=1234});
BinaryFormatter formatter=新的BinaryFormatter();
使用(var fs=new FileStream(“serialized.dat”、FileMode.Create、FileAccess.Write))
{
序列化(fs,(数据,数据));
序列化(fs,数据);
}
使用(var fs=new FileStream(“serialized.dat”、FileMode.Open、FileAccess.Read))
{
//创建代理选择器。
formatter.SurrogateSelector=Ref.AddTo(new-SurrogateSelector());
var(data1,data2)=((Ref,Ref))格式化程序。反序列化(fs);
var data3=(Ref)格式化程序。反序列化(fs);
//来自不同调用的对象引用不相同?
//根据文档,相同的格式化程序和ObjectedGenerator应该导致反序列化相同的对象。
Assert(ReferenceEquals(data1.Value,data2.Value),“来自同一调用的对象”);
Assert(ReferenceEquals(data1.Value,data3.Value),“来自不同调用的对象”);
}
}
}
从表面上看,这是不可能的,因为对象引用只对每个反序列化调用有意义。在内部,每个调用都会创建一个二进制文件来保存引用
如果你只想知道它们是否相等,你可以实现平等
[Serializable]
class Data
{
public int Value { get; set; }
public override bool Equals(object obj) =>
(obj is Data data) ? data.Value == Value : false;
public override int GetHashCode() => Value.GetHashCode();
}
这意味着:
Debug.Assert(Equals(data1, data2), "objects from same call");
Debug.Assert(Equals(data1, data3), "objects from different calls");
如果您绝对需要引用相等,可以通过实现,并记录结果,尝试在多个调用之间共享状态
更新:
好吧,如果让你白费力气去寻找一个可能根本不可行的解决方案,我会很难过。所以我自己试过:
让我们尝试添加一个类型,该类型通过将每个实例映射到一个唯一的id来包装引用等式,该id可以是GetHashCode
本身
[Serializable]
public class Ref<T>
{
public readonly T Value;
public readonly int Id;
public Ref(T value) { Value = value; Id = value.GetHashCode(); }
protected class RefSurrogate : ISerializationSurrogate
{
Dictionary<int, Ref<T>> Instances = new Dictionary<int, Ref<T>>();
public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
{
var refs = (Ref<T>)obj;
info.AddValue(nameof(Id), refs.Id);
if (!Instances.ContainsKey(refs.Id))
{
Instances.Add(refs.Id, refs);
info.AddValue(nameof(Value), refs.Value);
}
}
public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
{
var id = (int)info.GetInt32(nameof(Id));
if (Instances.TryGetValue(id, out var refs))
return refs;
else
return Instances[id] = new Ref<T>((T)info.GetValue(nameof(Value), typeof(T)));
}
}
public static SurrogateSelector AddTo(SurrogateSelector ss)
{
ss.AddSurrogate(typeof(Ref<T>), new StreamingContext(StreamingContextStates.All), new RefSurrogate());
return ss;
}
}
[可序列化]
公共类参考
{
公共只读T值;
公共只读int-Id;
公共引用(T值){value=value;Id=value.GetHashCode();}
受保护的类引用代理:ISerializationSubrogate
{
字典实例=新字典();
public void GetObjectData(对象对象对象、序列化信息、StreamingContext上下文)
{
var refs=(Ref)obj;
info.AddValue(name of(Id),refs.Id);
如果(!Instances.ContainsKey(refs.Id))
{
添加(refs.Id,refs);
信息添加值(名称(值)、参考值);
}
}
公共对象SetObjectData(对象对象对象、序列化信息、StreamingContext上下文、iUrrogateSelector选择器)
{
var id=(int)info.GetInt32(nameof(id));
if(Instances.TryGetValue(id,out var refs))
返回参考文献;
其他的
返回实例[id]=newref((T)info.GetValue(nameof(Value),typeof(T));
}
}
公共静态代理选择程序AddTo(代理选择程序ss)
{
艾德索尔