序列化链接的对象

时间:2014-01-06 09:44:55

标签: c# serialization delegates

我尝试为以下问题创建对象模型。 我需要一个文件夹对象(与目录文件夹相当)。每个文件夹可以包含其他子文件夹以及参数对象(与文件相当)。此外,每个参数都需要知道它所在的文件夹。到目前为止这很容易。所以我实施了以下工作解决方案。

我有一个基础对象,可以继承到文件夹或参数:

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

现在我创建了一个FolderEntry,它继承自Entry并支持通过实现IList&lt;&gt;来添加新的子条目。

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

现在这是我的问题。此外,还需要每个参数在托管列表中知道其索引。我改变了我的Entry-object,使其具有属性Index和委托GetIndex,其方式与Path和GetPath相同:

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

在Folder-object的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 +&lt;&gt; c__DisplayClass2”没有标记为可序列化。我看不出GetPath和GetIndex之间的明显区别。为了缩小范围,我从

替换了SetPathDelegate中创建的GetIndex委托的内容
entry.GetIndex = () => 
{
  return _entries.IndexOf(entry); 
};

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

令我惊讶的是,这又是可序列化的。为什么不导致我的GetPath委托有关序列化的任何问题,但我的GetIndex委托呢?

1 个答案:

答案 0 :(得分:1)

问题是您分配给GetIndex的匿名函数。在运行时,会创建一个新类型,该类型未标记为可序列化。

根据this post,您应该为格式化程序设置SurrogateSelector(有一些警告,详细阅读文章):

formatter.SurrogateSelector = new UnattributedTypeSurrogateSelector();

我在这里粘贴文章中的课程,供将来参考,以便彻底解答。

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