我有一个类,它包含所有现有实例的静态字典,这些字典在编译时定义。
基本上它看起来像这样:
[DataContract]
class Foo
{
private static Dictionary<long, Foo> instances = new Dictionary<long, Foo>();
[DataMember]
private long id;
public static readonly Foo A = Create(1);
public static readonly Foo B = Create(2);
public static readonly Foo C = Create(3);
private static Foo Create(long id)
{
Foo instance = new Foo();
instance.id = id;
instances.Add(instance);
return instance;
}
public static Foo Get(long id)
{
return instances[id];
}
}
还有其他字段,派生类,但这与问题无关。
仅序列化了id
。当反序列化此类型的实例时,我希望使用A
获取已创建为静态字段(B
,C
或Foo.Get(id)
)的实例而不是获得新的实例。
有一种简单的方法吗?我没有找到任何我能够理解的资源。
答案 0 :(得分:17)
在反序列化期间,它(AFAIK)总是使用一个新对象(FormatterServices.GetUninitializedObject
),但是为了让它在反序列化之后替换对象(但在它们被返回给调用者之前),你可以实现IObjectReference
,如下所示:
[DataContract]
class Foo : IObjectReference { // <===== implement an extra interface
object IObjectReference.GetRealObject(StreamingContext ctx) {
return Get(id);
}
...snip
}
完成......证明:
static class Program {
static void Main() {
Foo foo = Foo.Get(2), clone;
DataContractSerializer ser = new DataContractSerializer(typeof(Foo));
using (MemoryStream ms = new MemoryStream()) { // clone it via DCS
ser.WriteObject(ms, foo);
ms.Position = 0;
clone = (Foo)ser.ReadObject(ms);
}
Console.WriteLine(ReferenceEquals(foo, clone)); // true
}
}
请注意,对于MSDN上的部分信任方案here,有一些额外的注意事项。
答案 1 :(得分:3)
我遇到了类似的问题,我找到的最佳解决方案是添加一些包装类,即管理需要序列化的实例。
我不确定合同的确切签名。我使用了SerializableAttribute,并且看起来很像我。那样:
[Serializable]
class FooSerializableWrapper : ISerializable
{
private readonly long id;
public Foo Foo
{
get
{
return Foo.Get(id);
}
}
public FooSerializableWrapper(Foo foo)
{
id = foo.id;
}
protected FooSerializableWrapper(SerializationInfo info, StreamingContext context)
{
id = info.GetInt64("id");
}
void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("id", id);
}
}
答案 2 :(得分:0)
您可以使用OnDeserializingAttribute向所需内容迈进一步。但是,这可能只允许您设置属性(因此您可以拥有相当于使用静态实例填充当前实例的所有属性的Copy方法。
我想如果你真的想要返回你的静态实例,你可能不得不编写自己的反序列化器......
未经测试,但我认为你可以很容易地实现一个解串器:
public class MyDeserializer : System.Xml.Serialization.XmlSerializer
{
protected override object Deserialize(System.Xml.Serialization.XmlSerializationReader reader)
{
Foo obj = (Foo)base.Deserialize(reader);
return Foo.Get(obj.id);
}
}
请注意,您必须在获取ID时执行某些操作,因为它在您的代码中是私有的;此外,这假设您正在使用XML序列化;用你实际使用的任何东西替换继承。最后,这意味着您在反序列化对象时必须实例化此类型,这可能涉及更改某些代码和/或配置。
答案 3 :(得分:0)
没问题,只需使用2个班级。在getObject方法中,您将获得现有对象
[Serializable]
public class McRealObjectHelper : IObjectReference, ISerializable
{
Object m_realObject;
virtual object getObject(McObjectId id)
{
return id.GetObject();
}
public McRealObjectHelper(SerializationInfo info, StreamingContext context)
{
McObjectId id = (McObjectId)info.GetValue("ID", typeof(McObjectId));
m_realObject = getObject(id);
if(m_realObject == null)
return;
Type t = m_realObject.GetType();
MemberInfo[] members = FormatterServices.GetSerializableMembers(t, context);
List<MemberInfo> deserializeMembers = new List<MemberInfo>(members.Length);
List<object> data = new List<object>(members.Length);
foreach(MemberInfo mi in members)
{
Type dataType = null;
if(mi.MemberType == MemberTypes.Field)
{
FieldInfo fi = mi as FieldInfo;
dataType = fi.FieldType;
} else if(mi.MemberType == MemberTypes.Property){
PropertyInfo pi = mi as PropertyInfo;
dataType = pi.PropertyType;
}
try
{
if(dataType != null){
data.Add(info.GetValue(mi.Name, dataType));
deserializeMembers.Add(mi);
}
}
catch (SerializationException)
{
//some fiels are missing, new version, skip this fields
}
}
FormatterServices.PopulateObjectMembers(m_realObject, deserializeMembers.ToArray(), data.ToArray());
}
public object GetRealObject( StreamingContext context )
{
return m_realObject;
}
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
}
}
public class McRealObjectBinder: SerializationBinder
{
String assemVer;
String typeVer;
public McRealObjectBinder(String asmName, String typeName)
{
assemVer = asmName;
typeVer = typeName;
}
public override Type BindToType( String assemblyName, String typeName )
{
Type typeToDeserialize = null;
if ( assemblyName.Equals( assemVer ) && typeName.Equals( typeVer ) )
{
return typeof(McRealObjectHelper);
}
typeToDeserialize = Type.GetType( String.Format( "{0}, {1}", typeName, assemblyName ) );
return typeToDeserialize;
}
}
然后,反序列化时:
BinaryFormatter bf = new BinaryFormatter(null, context);
bf.Binder = new McRealObjectBinder(YourType.Assembly.FullName, YourType.FullName);
bf.Deserialize(memStream);