C#关于深拷贝的坑

14 篇文章 0 订阅

C#关于深拷贝的坑

问题及分析

目前遇到的一个问题:

有一个List<MyClass> target对象,需要把target备份一份,防止在界面中对target的内容修改后又取消,此时需要将target的数据进行还原,一开始时使用如下代码备份:

 class MyType
 {
     public string val = "origin";
 }
class Program
{
    static void Main(string[] args)
    {
        // 原始数据
        List<MyType> target = new List<MyType>() { new MyType()};
        // 进行备份
        var backup = target;
        // 修改原始数据
        target[0].val = "changed";
        target = backup;
        // 输出的是“changed”
        Console.WriteLine( target[0].val);
    }
}

这样写会发现修改了target之后,backup也发生了变化,这是因为使用=进行赋值,此时两个引用target、backup指向的是同一个地址,即同一个对象,而如果使用如下代码进行深拷贝之后,

 [Serializable]
class MyType
{
    public string val = "origin";
}
class Program
{
    //深拷贝代码
    public static List<T> Clone<T>(List<T> inputList)
    {
        BinaryFormatter Formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
        System.IO.MemoryStream stream = new System.IO.MemoryStream();
        Formatter.Serialize(stream, inputList);
        stream.Position = 0;
        var outList = Formatter.Deserialize(stream) as List<T>;
        stream.Close();
        return outList;
    }
    static void Main(string[] args)
    {
        List<MyType> target = new List<MyType>() { new MyType()};
        var backup = Clone<MyType>(target);
        target[0].val = "changed";
        target = backup;
        // 输出的是“origin”
        Console.WriteLine( target[0].val);
    }
}

此时修改后backup备份成功,但是此时还有一个更大的坑,如果在=进行还原之后又进行了修改,需要再次还原的话,会出现如下的情况:

 static void Main(string[] args)
 {
     List<MyType> target = new List<MyType>() { new MyType()};
     var backup = Clone<MyType>(target);
     target[0].val = "changed";
     target = backup;
     // 输出的是“origin”
     Console.WriteLine( target[0].val);
     
     // 在第一次还原之后又进行了修改
     target[0].val = "changed";
     target = backup;
     // 输出的是“changed”
     Console.WriteLine(target[0].val);

 }

在第一次还原之后又对target进行了修改,此时再进行还原,发现值发生了改变,并没有还原成功。此时的原因在于第一次还原后target赋值是使用的=,因此此时两个引用又指向了同一个地址,因此需要将代码进行如下修改:

  static void Main(string[] args)
  {
      List<MyType> target = new List<MyType>() { new MyType()};
      var backup = Clone<MyType>(target);
      target[0].val = "changed";
      // 更改处
      //target = backup;
      target = Clone<MyType>(backup);
      // 输出的是“origin”
      Console.WriteLine( target[0].val);
      target[0].val = "changed";
      // 更改处
      //target = backup;
      target = Clone<MyType>(backup);
      // 输出的是“origin”
      Console.WriteLine(target[0].val);

  }

修改的关键在于还原备份的时候也需要将=赋值改成深拷贝。可以看出,如果有多次修改还原的需求,不论在哪次还原,都需要进行深拷贝,防止下次修改的时候把备份的数据也进行了修改。

总结

考虑清楚什么时候需要对数据进行深拷贝,并且要清晰地认识到C#中引用类型传递的是地址,实质上操作的都是同一个对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值