2014-01-06 2 views
1

다음 문제에 대한 개체 모델을 만들려고합니다. 폴더 개체가 필요합니다 (디렉터리 폴더와 유사). 각 폴더에는 추가 하위 폴더 및 매개 변수 개체 (파일과 유사)가 포함될 수 있습니다. 또한 각 매개 변수는 해당 매개 변수가있는 폴더를 알아야합니다. 이것은 지금까지는 쉽습니다. 그래서 다음과 같은 작업 솔루션을 구현했습니다.연결된 개체 일련 화

는 I, 즉이 하나의 폴더 또는 파라미터에 상속 가능한베이스 오브젝트를 가지고

[Serializable()] 
public class Entry 
{ 
    public Func<string> GetPath; 
    public string Path 
    { 
    get 
    { 
     if (GetPath == null) return string.Empty; 
     return GetPath.Invoke(); 
    } 
    } 
} 
지금

I는 항목에서 상속은 IList < 구현하여 새로운 하위 항목을 추가 지원 FolderEntry을 작성> .

[Serializable()] 
class FolderEntry : Entry, IList<Entry> 
{ 
    private readonly List<Entry> _entries; 

    public FolderEntry() 
    { 
    _entries = new List<Entry>(); 
    } 

    public string FolderName { get; set; } 

    private void SetPathDelegate(Entry entry) 
    { 
    if (entry.GetPath != null) throw new ArgumentException("entry already assigned"); 

    entry.GetPath =() => 
    { 
     if (GetPath == null || string.IsNullOrEmpty(GetPath.Invoke())) return FolderName; 
     return GetPath.Invoke() + "|" + FolderName; 
    }; 
    } 

    public void Add(Entry item) 
    { 
    SetPathDelegate(item); 
    _entries.Add(item); 
    } 
    [...] 
} 

실행 취소/다시 실행 기능을 지원하기 위해 모든 클래스를 Serializable-Attribute를 추가하여 직렬화했습니다. 이 직렬화는 다음의 시험을 사용하여 지금까지 노력하고 있습니다 :

var folderA = new FolderEntry(); 
var folderB = new FolderEntry(); 

folderA.Add(folderB); 

var serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 
var memStream = new System.IO.MemoryStream(); 

serializer.Serialize(memStream, folderA); 

지금 여기 내 문제입니다. 또한 각 매개 변수가 호스팅 목록 내부의 색인을 알고 있어야한다는 필요성이 있습니다. 나는 부동산 지수와 이전 경로 및 GetPath와 같은 방법으로 대리인 GetIndex이 내 엔트리 객체를 변경 : 나는 새로운 대리자를 할당 폴더 객체의 SetPathDelegate 내부

[Serializable()] 
public class Entry 
{ 
    public Func<string> GetPath; 
    public string Path 
    { 
    get 
    { 
     if (GetPath == null) return string.Empty; 
     return GetPath.Invoke(); 
    } 
    } 

    public Func<int> GetIndex; 
    public int Index 
    { 
    get 
    { 
     if (GetIndex == null) return -1; 
     return GetIndex.Invoke(); 
    } 
    } 
} 

private void SetPathDelegate(Entry entry) 
{ 
    if (entry.GetPath != null) throw new ArgumentException("entry already assigned"); 
    if (entry.GetIndex != null) throw new ArgumentException("entry already assigned"); 

    entry.GetPath =() => 
    { 
    if (GetPath == null || string.IsNullOrEmpty(GetPath.Invoke())) return FolderName; 
    return GetPath.Invoke() + "|" + FolderName; 
    }; 

    entry.GetIndex =() => 
    { 
    return _entries.IndexOf(entry); 
    }; 
} 

이것을 직렬화하려고하면 어셈블리에서 내 "FolderEntry + <> c__DisplayClass2"가 직렬화 가능으로 표시되어 있지 않습니다. 나는 GetPath와 GetIndex 사이에 명백한 차이점을 볼 수 없다. 를 좁히려면, 나는이 다시 직렬화 내 놀랍게도

entry.GetIndex =() => 
{ 
    return -1; 
}; 

entry.GetIndex =() => 
{ 
    return _entries.IndexOf(entry); 
}; 

에서 SetPathDelegate에서 생성 된 GetIndex 위임의 내용을 대체했다. 왜 GetPath 대리자가 직렬화와 관련하여 문제를 일으키지 않지만 GetIndex 대리자는 문제가되지 않습니까?

답변

1

문제는 GetIndex에 할당 된 익명의 기능입니다. 런타임에 새 형식이 만들어지며 serializable로 표시되지 않습니다.

this post에 따르면, 당신은 (세부에있는 문서 읽기, 몇 가지주의와) 포맷터에 대한 SurrogateSelector을 설정해야합니다 나중에 참조 할 수 있도록 기사

formatter.SurrogateSelector = new UnattributedTypeSurrogateSelector(); 

I'me 붙여 넣기 여기에 수업을하고, 대답을 철저히하기 위해서.

public class UnattributedTypeSurrogate : ISerializationSurrogate 
{ 
    private const BindingFlags publicOrNonPublicInstanceFields = 
     BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; 

    public void GetObjectData(object obj, 
     SerializationInfo info, StreamingContext context) 
    { 
     var type = obj.GetType(); 
     foreach (var field in type.GetFields(publicOrNonPublicInstanceFields)) 
     { 
      var fieldValue = field.GetValue(obj); 
      var fieldValueIsNull = fieldValue != null; 
      if (fieldValueIsNull) 
      { 
       var fieldValueRuntimeType = fieldValue.GetType(); 
       info.AddValue(field.Name + "RuntimeType", 
        fieldValueRuntimeType.AssemblyQualifiedName); 
      } 

      info.AddValue(field.Name + "ValueIsNull", fieldValueIsNull); 
      info.AddValue(field.Name, fieldValue); 
     } 
    } 

    public object SetObjectData(object obj, 
     SerializationInfo info, StreamingContext context, ISurrogateSelector selector) 
    { 
     var type = obj.GetType(); 
     foreach (var field in type.GetFields(publicOrNonPublicInstanceFields)) 
     { 
      var fieldValueIsSerializable = info.GetBoolean(field.Name + "ValueIsNull"); 
      if (fieldValueIsSerializable) 
      { 
       var fieldValueRuntimeType = info.GetString(field.Name + "RuntimeType"); 
       field.SetValue(obj, 
        info.GetValue(field.Name, Type.GetType(fieldValueRuntimeType))); 
      } 
     } 

     return obj; 
    } 
} 

public class UnattributedTypeSurrogateSelector : ISurrogateSelector 
{ 
    private readonly SurrogateSelector innerSelector = new SurrogateSelector(); 
    private readonly Type iFormatter = typeof(IFormatter); 

    public void ChainSelector(ISurrogateSelector selector) 
    { 
     innerSelector.ChainSelector(selector); 
    } 

    public ISerializationSurrogate GetSurrogate(
     Type type, StreamingContext context, out ISurrogateSelector selector) 
    { 
     if (!type.IsSerializable) 
     { 
      selector = this; 
      return new UnattributedTypeSurrogate(); 
     } 
     return innerSelector.GetSurrogate(type, context, out selector); 
    } 

    public ISurrogateSelector GetNextSelector() 
    { 
     return innerSelector.GetNextSelector(); 
    } 
} 
+1

나는 그 부두교의 내부를 아직 이해하지 못하고 있지만 위대한 작품입니다. – Markus