8

我有一个类,它包含所有现有实例的静态字典,这些实例是在编译时定义的。

基本上它看起来像这样:

[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是序列化的。当反序列化这种类型的实例时,我想获取已创建为静态字段(ABC的实例,Foo.Get(id)而不是获取新实例。

有没有一种简单的方法可以做到这一点?我没有找到任何我能够理解的资源。

4

4 回答 4

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 上的部分信任方案,这里有一些额外的说明。

于 2010-01-01T23:43:06.547 回答
3

我有一个类似的问题,我发现最好的解决方案是添加一些包装类,即管理需要序列化的实例。

我不确定合同的确切签名。我使用了SerializableAttribute,并用它看起来smth。像那样:

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

}
于 2009-12-21T17:57:02.520 回答
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);
于 2012-11-23T06:23:54.990 回答
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 序列化;用您实际使用的任何内容替换继承。最后,这意味着您必须在反序列化对象时实例化此类型,这可能涉及更改某些代码和/或配置。

于 2009-12-21T18:18:07.703 回答