2009-12-21 1 views
8

나는 컴파일 타임에 정의 된 모든 기존 인스턴스의 정적 사전을 보유하는 클래스가 있습니다.C# DataContract Serialization, 기존 인스턴스에 deserialize하는 방법

[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 만 직렬화됩니다. 이 형식의 인스턴스를 deserialize 할 때 새 인스턴스를 가져 오는 대신 Foo.Get(id)을 사용하여 정적 필드 (A, B 또는 C)로 만든 인스턴스를 가져 오려고합니다.

간단한 방법이 있나요? 내가 이해할 수있는 자원을 찾지 못했습니다.

답변

16

그것을 (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에 대한 부분 신뢰 시나리오에서 이에 대한 몇 가지 추가 사항이 있습니다.

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

} 
+0

내가 실제로 사용하는 클래스가'Foo'는 매우 중앙 클래스이기 때문에, 해당과에서 사용하지 않을 : 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; } } 

그런 다음, 직렬화 때 기존 개체를 얻을 많은 다른 유형. –

+0

이 구현은 어떻게 생겼습니까? –

+0

내 편집 된 회신 – ironic

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 직렬화를 사용한다고 가정합니다. 상속을 실제로 사용하는 것으로 바꾸십시오. 마지막으로 이것은 객체를 비 직렬화 할 때이 유형을 인스턴스화해야한다는 것을 의미합니다. 이는 일부 코드 및/또는 구성 변경과 관련 될 수 있습니다. 직렬화 복원시

+0

'OnDeserializing' 속성을 알고 있습니다. 이미 작성된 인스턴스에서 호출되기 때문에 인스턴스 작성을 허용하지 않습니다. 'ISerializable' 인터페이스와 다른 것들도 있지만 이것에 대한 해결책을 찾을 수 없습니다. –

+0

자신의 디시리얼라이저를 만드는 방법에 대한 추가 정보를 참조하십시오. –

+0

WCF (NetDataContractSerializer)에 필요합니다. * 전체 응용 프로그램에 대해 다른 serializer를 사용할 수는 있지만 가능한 한 인프라를 유지하고 싶습니다. –

0

문제가 없으므로 단지 2 개의 클래스 만 사용하십시오.

BinaryFormatter bf = new BinaryFormatter(null, context); 
bf.Binder = new McRealObjectBinder(YourType.Assembly.FullName, YourType.FullName); 
bf.Deserialize(memStream);