【问题标题】:Serialization modifying contents of original object in C#在C#中序列化修改原始对象的内容
【发布时间】:2016-05-13 20:20:59
【问题描述】:

我在 C# 中使用复制构造函数实现了深度克隆方法。为了验证它是否有效,我通过比较序列化对象和它的克隆的结果来测试它。序列化是根据通用对象 T 完成的。我也根据具体对象进行了尝试,得到了相同的结果。

我有一种将对象序列化为字节数组的方法

private byte[] ObjectToBytes(T obj)
{
    BinaryFormatter formatter = new BinaryFormatter();
    using (MemoryStream stream = new MemoryStream())
    {
        formatter.Serialize(stream, obj);
        stream.Seek(0, SeekOrigin.Begin);
        return stream.ToArray();
    }
}

以下代码可以正常工作。

T original = this.GetNewThing();
T clone = original.DeepClone();

// serialize after cloning
byte[] originalBytes = ObjectToBytes(original);
byte[] cloneBytes = ObjectToBytes(clone);

bool equal = true;
for (int i = 0; i < originalBytes.Length; i++)
{
    if(originalBytes[i] != cloneBytes[i]
    {
        equal = false;
        break;
    }
}
equal == true; // True!

但是,当我切换对象序列化的顺序时,字节数组不再相等。

// serialize before cloning
T original = this.GetNewThing();
byte[] originalBytes = ObjectToBytes(original);

T clone = original.DeepClone();
byte[] cloneBytes = ObjectToBytes(clone);

bool equal = true;
for (int i = 0; i < originalBytes.Length; i++)
{
    if(originalBytes[i] != cloneBytes[i]
    {
        equal = false;
        break;
    }
}
equal == true; // False!

为什么序列化的顺序会影响这个?它与 BinaryFormatter 或 MemoryStream 对象有关吗?

编辑:

这是深度克隆方法的样子

public MyClass DeepClone()
{
    return new MyClass(this);
}

它使用的构造函数看起来像这样

protected MyClass(MyClass myClass)
{
    if (myClass == null)
        throw new ArguementNullException("myclass");

    this.number = myClass.Number;
    this.number2 = myClass.Number2;
    this.number3 = myClass.Number3;
}

对象绝不是复杂的。所有被复制的值都是值类型,所以没有需要担心的引用类型。

【问题讨论】:

  • 请发布 DeepClone 方法.. 可能有问题
  • 可能与对象的复杂性有关 - 看看这个:stackoverflow.com/questions/5017274/…
  • 认为我们可能还需要查看完整的MyClass。此外,它没有状态,那么为什么ObjectToBytes 是私有实例方法,而不是静态的?我认为,如果您可以格式化一个完整的示例代码,可以将其剪切/粘贴到例如 LinqPad 上,这可能会有所帮助。
  • 我刚刚在 LinqPad 中运行了这个,缺少的细节用我刚刚编写的最小示例填充。两种情况下的字节数组都是相等的。问题出在您未显示的某处,可能在类本身中,也可能是在 MyClass(MyClass myClass) 构造函数中调用的属性访问器中的逻辑。
  • 检查所有这些属性 - 可能是读取属性会改变状态。

标签: c# .net serialization


【解决方案1】:

DeepClone 或它依次调用的东西似乎正在改变您的源对象的状态。

【讨论】:

  • DeepClone 通过访问直到第一次访问才设置的属性来更改源对象。当我在创建字节数组之前进行克隆时,这很好,因为在深度克隆期间这两个属性都会被初始化。克隆前创建字节数组不会导致该属性被初始化,序列化也不会拾取初始化的值(该属性是值类型,所以取而代之的是默认值)。
【解决方案2】:

您必须修改 ObjectToBytes 方法如下:

private byte[] ObjectToBytes(Object obj)
    {
       if(obj == null)
          return null;

       BinaryFormatter bf = new BinaryFormatter();
       using(MemoryStream ms = new MemoryStream()) {
           bf.Serialize(ms, obj);

           return ms.ToArray();
       }
    }

希望对你有帮助

【讨论】:

  • 你能解释一下你认为这有什么帮助吗?
  • @adv12 因为在 .ToArray 之前更改流位置是一种不好的做法。这是一个尝试,但我希望看到 DeepClone 的实施。
  • 我在我的代码中删除了对Seek 的不必要调用。它没有解决问题。当我删除它时它也没有造成任何问题,所以我将它排除在外。
  • 你要处理掉那个MemoryStream
  • 内存流是否被释放并不重要。 The only thing it does 将流标记为关闭且不可写
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-09-11
  • 2020-04-24
  • 2015-09-16
  • 1970-01-01
  • 2012-07-18
  • 2022-01-07
相关资源
最近更新 更多