2010-04-26 9 views
3

내 자신의 IFormatter 구현을 작성 중이므로 둘 다 ISerializable을 구현하는 두 형식 사이에서 순환 참조를 해결할 방법을 생각할 수 없습니다. I에는 직렬화 및 deserialise의 B, 위 테스트하기 위해 아웃 - 오브 - 박스 경우 BinaryFormatter를 사용하는 경우ISerializable을 구현하는 개체에 대한 순환 참조 해결

Bar b = new Bar(); 
Foo f = new Foo(b); 
bool equal = ReferenceEquals(b, b.Foo.Bar); // true 

// Serialise and deserialise b 

equal = ReferenceEquals(b, b.Foo.Bar); 

:

[Serializable] 
class Foo : ISerializable 
{ 
    private Bar m_bar; 

    public Foo(Bar bar) 
    { 
     m_bar = bar; 
     m_bar.Foo = this; 
    } 

    public Bar Bar 
    { 
     get { return m_bar; } 
    } 

    protected Foo(SerializationInfo info, StreamingContext context) 
    { 
     m_bar = (Bar)info.GetValue("1", typeof(Bar)); 
    } 

    public void GetObjectData(SerializationInfo info, StreamingContext context) 
    { 
     info.AddValue("1", m_bar); 
    } 
} 

[Serializable] 
class Bar : ISerializable 
{ 
    private Foo m_foo; 

    public Foo Foo 
    { 
     get { return m_foo; } 
     set { m_foo = value; } 
    } 

    public Bar() 
    { } 

    protected Bar(SerializationInfo info, StreamingContext context) 
    { 
     m_foo = (Foo)info.GetValue("1", typeof(Foo)); 
    } 

    public void GetObjectData(SerializationInfo info, StreamingContext context) 
    { 
     info.AddValue("1", m_foo); 
    } 
} 

내가 다음을 수행하십시오

여기에 일반적인 패턴이다 참조 평등은 기대했던대로 true를 반환합니다. 하지만 내 사용자 정의 IFormatter에서 이것을 달성 할 수있는 방법을 생각할 수 없습니다.

비 ISerializable 상황에서 간단히 대상 참조가 해결되면 반사를 사용하여 "보류 중"개체 필드를 다시 방문 할 수 있습니다. 그러나 ISerializable을 구현하는 객체의 경우 SerializationInfo를 사용하여 새 데이터를 주입 할 수 없습니다.

누구나 올바른 방향으로 나를 가리킬 수 있습니까?

답변

0

개체 그래프에서 동일한 개체를 두 번 이상 사용했는지, 출력에서 ​​각 개체에 태그를 지정했는지 감지해야합니다. 발생 횟수가 2 이상이되면 "참조"를 출력해야합니다. 개체 대신 기존 태그를 한 번 더 추가합니다. 직렬화

의사 코드 : 직렬화 복원

for each object 
    if object seen before 
     output tag created for object with a special note as "tag-reference" 
    else 
     create, store, and output tag for object 
     output tag and object 

의사 코드 :

while more data 
    if reference-tag to existing object 
     get object from storage keyed by the tag 
    else 
     construct instance to deserialize into 
     store object in storage keyed by deserialized tag 
     deserialize object 

당신이, 그래서 그들이 지정하고이 마지막 단계를 순서대로 수행하는 것이 중요하다 이 경우 처리 할 수 ​​있습니다.

SomeObject obj = new SomeObject(); 
obj.ReferenceToSomeObject = obj; <-- reference to itself 

예. 객체를 비 직렬화 한 후에 저장소에서 객체에 대한 참조가 필요할 수 있기 때문에 객체를 태그에 저장하는 것은 불가능합니다.

+0

본인은 "참조 태그"에 대한 귀하의 요지를 알고 있으며 제 형식 기는이 기술을 이미 사용하고 있습니다. 그러므로 당신의 자기 참조 예제는 저에게 문제가되지 않습니다. 그러나 당신의 답이 어떻게 서로를 참조하는 ISerializable 구현 객체로 나를 돕는 지 알지 못합니다. 이 특정 문제를 해결할 수 있습니까? 감사. – Chris

+0

무슨 뜻인지 전혀 모르겠다. 직렬화와 관련된 개인 생성자를 사용하는 것에 대해 이야기하고 있습니까? –

+0

예, 정확하게. ISerializable을 구현 한 객체를 확장하는 유일한 방법은 특수 생성자를 호출하는 것입니다. – Chris

5

이 상황이 FormatterServices.GetUninitializedObject 방법의 이유입니다. 일반적인 아이디어는 당신이 그들의 SerializationInfo에서 서로를 참조하는 객체 A와 B가있는 경우 다음과 같이 당신이 그들을 역 직렬화 할 수 있다는 것입니다 :

(이 설명의 목적을 위해, (SI,SC)는 하나, 즉 유형의 직렬화 생성자를 참조 이는 SerializationInfoStreamingContext 걸린다.)

  1. 먼저 직렬화 할 하나의 객체를 선택. 가치 유형 인 것을 선택하지 않는 한 선택하는 것이 중요하지 않습니다. 당신이 아직 (SI,SC) 생성자를 호출 할 준비가되어 있지 때문에, A. GetUninitializedObject에가 (초기화하지만) 할당
  2. 전화 A의 형식의 인스턴스를 선택 말할 수 있습니다.
  3. 일반적인 방식으로 B를 빌드합니다. 즉, SerializationInfo 개체를 만듭니다 (이제 절반 반전 된 A에 대한 참조를 포함). B의 (SI,SC) 생성자에 전달합니다.
  4. 이제 모든 종속성을 가지고 은 할당 된 A 개체 인을 초기화합니다. 그것의 SerializationInfo 개체를 만들고 A의 (SI,SC) 생성자를 호출하십시오. 리플렉션을 통해 기존 인스턴스에서 생성자를 호출 할 수 있습니다.

GetUninitializedObject 메서드는 순수한 CLR 마법입니다.이 인스턴스를 생성하기 위해 생성자를 호출하지 않고 인스턴스를 만듭니다. 기본적으로 모든 필드를 0/null로 설정합니다.

(SI,SC) 생성자에서 하위 개체의 멤버를 사용하지 않도록주의해야하는 이유입니다. 하위 개체는 할당되었지만 아직 초기화되지 않았을 수 있습니다. 이것은 또한 모든 객체 초기화가 수행 된 후 및 직렬화되지 않은 객체 그래프가 반환되기 전에 자식 객체를 사용할 수있는 기회를 제공하는 IDeserializationCallback 인터페이스의 이유이기도합니다.

ObjectManager 클래스는이 모든 작업 (및 다른 유형의 수정 작업)을 수행 할 수 있습니다. 그러나, 난 deserialization의 복잡성을 감안할 때 항상 문서화되지 않았기 때문에 적절하게 사용하는 방법을 찾아내는 데 시간을 낭비하지 않았습니다.생성자를 더 빨리 호출하기 위해 최적화 된 내부 CLR 리플렉션을 사용하여 4 단계를 수행하기 위해 좀 더 많은 마법을 사용합니다 (공개 방법의 약 2 배로 시간을 맞췄습니다).

마지막으로, 역 직렬화가 불가능한 사이클을 포함하는 객체 그래프가 있습니다. 한 예는 두 개의주기가있는 경우입니다 (나는 BinaryFormatter을 테스트했으며 예외를 throw합니다). 또 하나 의심되는 점은 cycle involving nothing but boxed value-types입니다.