2014-01-06 48 views
1

我嘗試爲以下問題創建對象模型。 我需要一個文件夾對象(與目錄文件夾相媲美)。每個文件夾可以包含額外的子文件夾和附加參數對象(與文件相當)。另外,每個參數需要知道它駐留在哪個文件夾中。到目前爲止,這很容易。所以我實施了以下工作解決方案。序列化鏈接對象

我有一個基礎對象,即可以被繼承到一個文件夾或一個參數:

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

現在我創建了一個FolderEntry,即從條目繼承,並支持通過實現的IList <添加新的子條目> 。

[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:

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

文件夾對象的SetPathDelegate裏面給我分配了新的委託

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

如果我嘗試序列化這個,我期待Assembly ...中的「FolderEntry + <> c__DisplayClass2」未被標記爲可序列化。我無法看到GetPath和GetIndex之間的明顯區別。爲了縮小範圍,我取代了SetPathDelegate創建GetIndex委託的內容從

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

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

令我驚訝這又是序列化的。爲什麼不導致我的GetPath委託關於序列化的任何問題,但我的GetIndex委託呢?

回答

1

問題是您分配給GetIndex的匿名函數。在運行時,將創建一個新類型,該類型未標記爲可序列化。

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