C# 第十五弹 —————— 浅拷贝和深拷贝

17 篇文章 1 订阅

说实话,我也挺晕的,弄了好久才明白过来。

先说一下浅拷贝吧。

浅拷贝分为两种情况,一种是拷贝值类型,一种是拷贝引用类型。

当浅拷贝值类型的时候,浅拷贝就相当于复制一份值出来,注意,这里是值而不是引用,所以当复制出来之后,复制出来的值就与源值是没有关系的了。贴一段代码吧:

类 Test:

[Serializable]
class Test
{
    public int i = 0;
    public int[] iArr = { 1, 2, 3 };
    public String s = "clone";
    public Test Clone1() //浅拷贝
    {
        return this.MemberwiseClone() as Test;
    }

    public Test Clone2() //深拷贝
    {
        MemoryStream stream = new MemoryStream();
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, this);
        stream.Position = 0;
        return formatter.Deserialize(stream) as Test;
    }
}

类:Program

class Program
{
    static void Main(string[] args)
    {
         Test t = new Test();
         Test t1 = t.Clone1();
         Test t2 = t.Clone2();
         t1.i = 10;
         Console.WriteLine("t:{0},  t1:{1},  t2:{2}", t.i, t1.i, t2.i);
         Console.ReadKey();
    }
}

结果:

这里会发现当浅拷贝值类型的时候,源值是没有发生改变的,所以这也说明了,当浅拷贝值类型的时候是复制一份出来了,与源值是没有关系的,当然深拷贝也是没有改变的,这很正常,下面会说的。

但是当浅拷贝引用类型的时候,浅拷贝就相当于复制一份引用出来,而地址是不变的还是那个地址,所以当复制出来之后,复制出来的引用改变了,所对应的堆里的数据也是会跟着改变的,源值也会跟着改变。贴一段代码。

类 Test:

[Serializable]
class Test
{
    public int i = 0;
    public int[] iArr = { 1, 2, 3 };
    public String s = "clone";
    public Test Clone1() //浅拷贝
    {
        return this.MemberwiseClone() as Test;
    }

    public Test Clone2() //深拷贝
    {
        MemoryStream stream = new MemoryStream();
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, this);
        stream.Position = 0;
        return formatter.Deserialize(stream) as Test;
    }
}

类 Program:

class Program
{
    static void Main(string[] args)
    {
         Test t = new Test();
         Test t1 = t.Clone1();
         Test t2 = t.Clone2();
         t1.iArr[0] = 100;

         Console.WriteLine("iArr Clone");
         foreach (var v in t.iArr)
         {
             Console.WriteLine(v);
         }
         Console.WriteLine("iArr Clone1");
         foreach (var v in t1.iArr)
         {
             Console.WriteLine(v);
         }
         Console.WriteLine("iArr Clone2");
         foreach (var v in t2.iArr)
         {
             Console.WriteLine(v);
         }
         Console.ReadKey();
    }
}

结果:

这里会发现当t1的iArr[0]发生改变的时候,源值iArr[0]也改变了,而t2的iArr[0]是没有任何变化的。

但是,注意这个但是啊!

string类型是引用类型,这是大家都知道的吧,不用我说吧,enmmmm,不知道的人可以查一下。

当浅拷贝string类型的时候,当拷贝出来的值发生改变的时候,源值也是会发生变化的,是不是很不可思议。没错,就是这么不可思议,贴上代码。

类 Test:

[Serializable]
class Test
{
    public int i = 0;
    public int[] iArr = { 1, 2, 3 };
    public String s = "clone";
    public Test Clone1() //浅拷贝
    {
        return this.MemberwiseClone() as Test;
    }

    public Test Clone2() //深拷贝
    {
        MemoryStream stream = new MemoryStream();
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, this);
        stream.Position = 0;
        return formatter.Deserialize(stream) as Test;
    }
}

类 Program:

class Program
{
    static void Main(string[] args)
    {
        Test t = new Test();
        Test t1 = t.Clone1();
        Test t2 = t.Clone2();
            
        t1.s = "copy";

        Console.WriteLine("t:{0},  t1:{1},  t2:{2}", t.s, t1.s, t2.s);
        Console.WriteLine("");

        Console.ReadKey();
    }
}

结果:

这里会发现当t1发生变化的情况下,源值t居然没发生变化,按理说应该是会有变化的,别急,下面有说法。

原因就是:

我们看一下string的源代码就可以理解了

//表示空字符串。此字段为只读。
public static readonly string Empty;

string类型是只读的,当string值改变的时候,会重新分配地址的哦,这样子就与源值是没有任何关系的了。

那么现在说说深拷贝吧!

深拷贝就是拷贝了,无论是值类型还是引用类型,改变拷贝出来的数据都不会影响源值的数据的。

好了就说到这里啦,有疑问的或者感觉我说的不对的地方,欢迎指导哈。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
C#的序列化和反序列化是将对象转换为二进制数据或将二进制数据转换为对象的过程。在C#中,可以使用BinaryFormatter、XmlSerializer、DataContractSerializer等多种方式进行序列化和反序列化。 拷贝拷贝用于复制对象。拷贝只复制对象的引用,而不会复制对象的实际内容。拷贝则会完全复制对象的内容,包括引用类型的成员变量。 在C#中,可以通过实现ICloneable接口来实现拷贝。对于拷贝,则需要自己手动实现一个递归复制对象的方法。 下面是一个示例代码,演示了如何进行序列化、反序列化以及拷贝拷贝: ```csharp using System; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Xml.Serialization; [Serializable] public class Person : ICloneable { public string Name { get; set; } public int Age { get; set; } public Address Address { get; set; } public object Clone() { return new Person { Name = Name, Age = Age, Address = (Address)Address?.Clone() }; } } [Serializable] public class Address : ICloneable { public string City { get; set; } public string Street { get; set; } public object Clone() { return new Address { City = City, Street = Street }; } } public static class SerializationHelper { public static byte[] SerializeToBinary(object obj) { using (var ms = new MemoryStream()) { var formatter = new BinaryFormatter(); formatter.Serialize(ms, obj); return ms.ToArray(); } } public static T DeserializeFromBinary<T>(byte[] data) { using (var ms = new MemoryStream(data)) { var formatter = new BinaryFormatter(); return (T)formatter.Deserialize(ms); } } public static string SerializeToXml(object obj) { using (var writer = new StringWriter()) { var serializer = new XmlSerializer(obj.GetType()); serializer.Serialize(writer, obj); return writer.ToString(); } } public static T DeserializeFromXml<T>(string xml) { using (var reader = new StringReader(xml)) { var serializer = new XmlSerializer(typeof(T)); return (T)serializer.Deserialize(reader); } } } public static class ObjectHelper { public static T ShallowCopy<T>(this T obj) { return (T)obj.MemberwiseClone(); } public static T DeepCopy<T>(this T obj) { using (var ms = new MemoryStream()) { var formatter = new BinaryFormatter(); formatter.Serialize(ms, obj); ms.Position = 0; return (T)formatter.Deserialize(ms); } } } // 示例代码 var person1 = new Person { Name = "Tom", Age = 30, Address = new Address { City = "Beijing", Street = "Chang'an Avenue" } }; // 拷贝 var person2 = person1.DeepCopy(); person2.Name = "Jerry"; person2.Address.City = "Shanghai"; Console.WriteLine($"person1.Name={person1.Name}, person1.Address.City={person1.Address.City}"); Console.WriteLine($"person2.Name={person2.Name}, person2.Address.City={person2.Address.City}"); // 拷贝 var person3 = person1.ShallowCopy(); person3.Name = "John"; person3.Address.City = "Guangzhou"; Console.WriteLine($"person1.Name={person1.Name}, person1.Address.City={person1.Address.City}"); Console.WriteLine($"person3.Name={person3.Name}, person3.Address.City={person3.Address.City}"); // 序列化和反序列化 var data = SerializationHelper.SerializeToBinary(person1); var person4 = SerializationHelper.DeserializeFromBinary<Person>(data); Console.WriteLine($"person1.Name={person1.Name}, person1.Address.City={person1.Address.City}"); Console.WriteLine($"person4.Name={person4.Name}, person4.Address.City={person4.Address.City}"); var xml = SerializationHelper.SerializeToXml(person1); var person5 = SerializationHelper.DeserializeFromXml<Person>(xml); Console.WriteLine($"person1.Name={person1.Name}, person1.Address.City={person1.Address.City}"); Console.WriteLine($"person5.Name={person5.Name}, person5.Address.City={person5.Address.City}"); ``` 输出结果: ``` person1.Name=Tom, person1.Address.City=Beijing person2.Name=Jerry, person2.Address.City=Shanghai person1.Name=Tom, person1.Address.City=Guangzhou person3.Name=John, person3.Address.City=Guangzhou person1.Name=Tom, person1.Address.City=Beijing person4.Name=Tom, person4.Address.City=Beijing person1.Name=Tom, person1.Address.City=Beijing person5.Name=Tom, person5.Address.City=Beijing ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值