c#:浅克隆和深克隆,序列化和反序列化

一、浅克隆和深克隆(浅复制和深复制)
浅克隆和深克隆最典型的应用是数据集对象DataSet的Clone和Copy方法。Clone()方法用来复制DataSet的结构,但是不复制DataSet的数据,实现了浅复制。Copy()方法不但复制结构,也复制数据,实现了深复制。另外:我们熟知的Object对象有一个MemberwiseClone()方法,它实现的就是浅表复制。该方法不是虚拟的,所以不能重写它的实现代码。
1、浅克隆:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。
Code is cheap.看代码:
(1)值类型的浅克隆


using System;

namespace ShallowColone
{
    /*浅克隆 只有值类型的情况*/
    class SourceClass : ICloneable //继承自ICloneable,创建作为当前实例副本的新对象。
    {
        public SourceClass(string name,int id)
        {
            this.name = name;
            this.id = id;
        }
        private string name;
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        private int id;

public int Id
        {
            get { return id; }
            set { id = value; }
        }

public object Clone()
        {
            return this.MemberwiseClone();
        }
    }

class Test
    {
        static void Main()
        {
            SourceClass test = new SourceClass("source class",1);
            SourceClass test2 = (SourceClass)test.Clone();
            Console.WriteLine("test name:" + test.Name);
            Console.WriteLine("test2 name:" + test2.Name);
            Console.WriteLine("test id:" + test.Id);
            Console.WriteLine("test2 id:" + test2.Id);
            if (test.Name == test2.Name && test.Id == test2.Id)
            {
                Console.WriteLine("See,the value after the clone method is the same!");
            }
            Console.WriteLine(object.ReferenceEquals(test, test2)); //对象的一个副本,内存地址不是指向同一个引用
            test2.Name = "copyied class";
            Console.WriteLine("test name:" + test.Id);
            Console.WriteLine("test2 name:" + test2.Id);
            Console.ReadLine();
        }
    }
}

ps:注意string类型是比较特殊的值类型
(2)引用类型的浅克隆


using System;

namespace ShallowColone
{
    class User
    {
        private string name;
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
    }

/*浅克隆 值类型和引用类型的情况*/
    class SourceClass : ICloneable //继承自ICloneable,创建作为当前实例副本的新对象。
    {
        public SourceClass(string name,int id)
        {
            this.name = name;
            this.id = id;
            testUser = new User();//初始化一个引用对象
        }
        private string name;
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        private int id;

public int Id
        {
            get { return id; }
            set { id = value; }
        }

private User testUser;

public void SetUser(string userName)
        {
            testUser.Name = userName;
        }

public object Clone()
        {
            return this.MemberwiseClone();
        }
        public void Display()
        {
            Console.WriteLine("Hi,i am " + testUser.Name);
        }
    }

class Test
    {
        static void Main()
        {

SourceClass test = new SourceClass("source class", 1);
            test.SetUser("Jeff Wong");

SourceClass test2 = (SourceClass)test.Clone();

test2.SetUser("jeff");

SourceClass test3 = (SourceClass)test.Clone();

test3.SetUser("jeff2");

//下面说明三个实例化对象都是指向同一个引用(最后的那个人jeff2)
            test.Display();
            test2.Display();
            test3.Display();

Console.WriteLine("See,the reference after the clone method is the same!");
            Console.ReadLine();
        }
    }
}

2、深克隆:对于值类型,被复制对象的所有变量都含有与原来的对象相同的值,把要复制的对象的引用的对象都复制一遍。
也就是说,深克隆的值类型和浅克隆一样,但是对于引用类型,深克隆把引用对象的变量指向复制过的新对象,而不是原有的引用的对象。下面改进“引用类型的浅复制”中的示例来说明深克隆的概念:


using System;

namespace ShallowColone
{
    class User : ICloneable //用户也继承自ICloneable接口
    {
        private string name;
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

public object Clone()
        {
            return (User)this.MemberwiseClone();
        }
    }

/*浅克隆 值类型和引用类型的情况*/
    class SourceClass : ICloneable //继承自ICloneable,创建作为当前实例副本的新对象。
    {
        public SourceClass(string name,int id)
        {
            this.name = name;
            this.id = id;
            testUser =new User();//***初始化一个引用对象,clone方法***
        }
        /// <summary>
        /// 提供clone方法调用的私有构造函数,以便克隆用户信息
        /// </summary>
        /// <param name="testUser"></param>
        private SourceClass(User testUser)
        {
            this.testUser = (User)testUser.Clone();
        }
        private string name;
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        private int id;

public int Id
        {
            get { return id; }
            set { id = value; }
        }

private User testUser;

public void SetUser(string userName)
        {
            testUser.Name = userName;
        }

/// <summary>
        /// 深克隆 改进后的克隆方法
        /// </summary>
        /// <returns></returns>
        public object Clone()
        {
            SourceClass retClass = new SourceClass(this.testUser);//调用私有构造函数,让用户克隆完成
            retClass.id = this.id;
            retClass.name = this.name;
            return retClass;
        }
        public void Display()
        {
            Console.WriteLine("Hi,i am " + testUser.Name);
        }
    }

class Test
    {
        static void Main()
        {

SourceClass test = new SourceClass("source class", 1);
            test.SetUser("Jeff Wong");

SourceClass test2 = (SourceClass)test.Clone();

test2.SetUser("jeff");

SourceClass test3 = (SourceClass)test.Clone();

test3.SetUser("jeff2");

//经过深克隆,达到我们设置不同用户的初衷
            test.Display();
            test2.Display();
            test3.Display();

Console.WriteLine("See,the reference after the clone method is the NOT same!");
            Console.ReadLine();
        }
    }
}

二、序列化和反序列化
前言: 当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为对象。 把对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为对象的过程称为对象的反序列化。
这里还要注意对象的序列化主要有两种用途:1) 在网络上传送对象的字节序列。2) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中。
.net为我们主要提供了三种序列化方式:

它们的主要区别请参考下图:

最后来三段代码,简单实现常用的序列化和反序列化。
1、使用BinaryFormatter


using System;
using System.Data;
using System.Configuration;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace BinarySerialize
{
    //要使一个类可序列化,最简单的方法是使用Serializable 属性对它进行标记
    [Serializable]
    public class User //要序列化的用户类
    {
        public int tid = 252;
        public string name = "jeff wong";
        [NonSerialized]
        public string sex = "male"; // 性别被标识为不可序列化
    }
    public class SerializeUtil
    {
       private static string strFile = "c:\\User.data";

public static void Serialize(User testUser)
        {
            using (FileStream fs = new FileStream(strFile, FileMode.Create))
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(fs, testUser);
            }
        }

public static User DeSerialize()
        {
            User testUser = null;
            using (FileStream fs = new FileStream(strFile, FileMode.Open))
            {
                BinaryFormatter formatter = new BinaryFormatter();
                testUser = (User)formatter.Deserialize(fs);
            }
            return testUser;
        }
    }

public class Program
    {
        static void Main()
        {
            SerializeUtil.Serialize(new User());//序列化
            User tmpUser = SerializeUtil.DeSerialize();//反序列化
            Console.WriteLine(tmpUser.tid + "-" + tmpUser.name);
            Console.WriteLine(tmpUser.sex); //sex被标识为不可序列化,所以总为null
            Console.ReadLine();
        }
    }
}

2、使用SoapFormatter
 先添加System.Runtime.Serialization.Formatters.Soap引用,然后和BinaryFormatter类似,我们只需要做一下简单修改即可:
 a.将using语句中的.Formatter.Binary改为.Formatter.Soap;
 b.将所有的BinaryFormatter替换为SoapFormatter.
 c.确保报存文件的扩展名为.xml
 经过上面简单改动,即可实现SoapFormatter的串行化,这时候产生的文件就是一个xml格式的文件。


using System;
using System.Data;
using System.Configuration;
using System.IO;
using System.Runtime.Serialization.Formatters.Soap;

namespace SoapSerialize
{
    //要使一个类可序列化,最简单的方法是使用Serializable 属性对它进行标记
    [Serializable]
    public class User //要序列化的用户类
    {
        public int tid = 252;
        public string name = "jeff wong";
        [NonSerialized]
        public string sex = "male"; // 性别被标识为不可序列化
    }
    public class SerializeUtil
    {
       private static string strFile = "c:\\User.xml"; //存为xml

public static void Serialize(User testUser)
        {
            using (FileStream fs = new FileStream(strFile, FileMode.Create))
            {
                SoapFormatter formatter = new SoapFormatter();
                formatter.Serialize(fs, testUser);
            }
        }

public static User DeSerialize()
        {
            User testUser = null;
            using (FileStream fs = new FileStream(strFile, FileMode.Open))
            {
                SoapFormatter formatter = new SoapFormatter();
                testUser = (User)formatter.Deserialize(fs);
            }
            return testUser;
        }
    }

public class Program
    {
        static void Main()
        {
            SerializeUtil.Serialize(new User());//序列化
            User tmpUser = SerializeUtil.DeSerialize();//反序列化
            Console.WriteLine(tmpUser.tid + "-" + tmpUser.name);
            Console.WriteLine(tmpUser.sex); //sex被标识为不可序列化,所以总为null
            Console.ReadLine();
        }
    }
}

(3)、使用XmlSerializer
想使用XmlSeralizer进行串行化我们需要做一下修改:
 a.添加System.Xml.Serialization命名空间.
 b.Serializable和NoSerialized属性将被忽略,而是使用XmlIgnore属性,它的行为与NoSerialized类似.
 c.XmlSeralizer要求类有个默认的构造器.


using System;
using System.Data;
using System.Configuration;
using System.IO;
using System.Xml.Serialization;

namespace XmllSerialize
{
    [Serializable]
    public class User //要序列化的用户类
    {
        public int tid = 252;
        public string name;
        [XmlIgnore]
        public string sex = "male"; 
       
        public User(string name)
        {
            this.name = name;
        }
        /// <summary>
        /// 必须注意:XmlSeralizer要求类有个默认的构造器
        /// </summary>
        public User()
        { 
        }
    }
    public class SerializeUtil
    {
       private static string strFile = "c:\\User.xml"; //存为xml

public static void Serialize(User testUser)
        {
            XmlSerializer xs = new XmlSerializer(typeof(User));
            using (Stream stream = new FileStream(strFile, FileMode.Create, FileAccess.Write, FileShare.Read))
            {
                xs.Serialize(stream, testUser);
            }
        }

public static User DeSerialize()
        {
            User testUser = null;
            using (FileStream fs = new FileStream(strFile, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                XmlSerializer xs = new XmlSerializer(typeof(User));
                testUser = (User)xs.Deserialize(fs);
            }

return testUser;
        }
    }

public class Program
    {
        static void Main()
        {
            SerializeUtil.Serialize(new User("jeff wong"));//序列化
            User tmpUser = SerializeUtil.DeSerialize();//反序列化
            Console.WriteLine(tmpUser.tid + "-" + tmpUser.name);
            Console.WriteLine(tmpUser.sex);
            Console.ReadLine();
        }
    }
}

关于常用的XML序列化,可以通过如下通用泛型方法结合扩展方法轻松进行序列化和反序列化操作,代码如下:

using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

public static class XMLSerializeExtension
{
    public static string Serialize<T>(this T value)
    {
        var result = string.Empty;
        if (value == null)
        {
            return result;
        }
        try
        {
            var serializer = new XmlSerializer(typeof(T));
            var stringWriter = new StringWriter();
            using (var writer = XmlWriter.Create(stringWriter))
            {
                serializer.Serialize(writer, value);
                result = stringWriter.ToString();
            }
        }
        catch (Exception ex)
        {
            throw new Exception("An error occurred while XML serialize", ex);
        }

        return result;
    }

    public static T Deserialize<T>(this string value)
    {
        T retObj = default(T);
        if (string.IsNullOrWhiteSpace(value) == true)
        {
            return retObj;
        }
        try
        {
            using (var rdr = new StringReader(value))
            {
                var serializer = new XmlSerializer(typeof(T));
                retObj = (T)serializer.Deserialize(rdr);
            }
        }
        catch (Exception ex)
        {
            throw new Exception("An error occurred while XML Deserialize", ex);
        }

        return retObj;
    }

}

必须注意,XmlSerializer要慎用, 容易引起动态加载dll引发内存泄漏,这里是内存泄漏的例子。

时间: 2024-10-13 05:12:41

c#:浅克隆和深克隆,序列化和反序列化的相关文章

Java对象的浅克隆和深克隆

为什么需要克隆 在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能会需要一个和A完全相同新对象B, 并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的.在Java语言中,用简单的赋值语句是不 能满足这种需求的,要满足这种需求有很多途径.     克隆的实现方式   一.浅度克隆      浅度克隆对于要克隆的对象,对于其基本数据类型的属性,复制一份给新产生的对象,对于非基本数据类型的属性,仅仅

java(30) - 对象浅克隆和深克隆

一.浅克隆和深克隆的概念:        1).浅克隆:又称为浅复制,被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换而言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象. 2).深克隆:又称为深复制,被复制的对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量.那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象.换而言之,深复制就是把要复制的对象所引用的对象都复制了一遍. 二.浅克隆实现: <pre

二叉树的序列化和反序列化

http://blog.csdn.net/qq_27703417/article/details/70958692 先序遍历二叉树,如果遇到空节点,就在str的末尾加上"#!","#"表示这个节点为空,节点值不存在,当然你也可以用其他的特殊字符,"!"表示一个值的结束.如果遇到不为空的节点,假设节点值为3,就在str的末尾加上"3!".现在请你实现树的先序序列化. 先序遍历 import java.util.*; //使用递归

PHP中的抽象类与抽象方法/静态属性和静态方法/PHP中的单利模式(单态模式)/串行化与反串行化(序列化与反序列化)/约束类型/魔术方法小结

  前  言  OOP  学习了好久的PHP,今天来总结一下PHP中的抽象类与抽象方法/静态属性和静态方法/PHP中的单利模式(单态模式)/串行化与反串行化(序列化与反序列化). 1  PHP中的抽象类与抽象方法 1.什么是抽象方法?              没有方法体 {} 的方法,必须使用abstract 关键字修饰.这样的方,我们叫做抽象方法.                    abstract function say(); //    抽象方法 2.什么是抽象类?        

序列化与反序列化

对象的序列化,反序列化 1)对象序列化,就是将Object转化为byte序列,反之叫对象的反序列化 2)序列化流(ObjectOutputStream),是过滤流----writeObject() 反序列化流(ObjectInputStream)------readObject() 3)序列化接口(Serializable) 对象必须实现序列化接口,才能进行序列化,否则将出现异常 这个接口,没有任何方法,只是一个标准

java序列化与反序列化

public interface Serializable类通过实现 java.io.Serializable 接口以启用其序列化功能.未实现此接口的类将无法使其任何状态序列化或反序列化.可序列化类的所有子类型本身都是可序列化的.序列化接口没有方法或字段,仅用于标识可序列化的语义. 序列化就是将一个对象的状态(各个属性量)保存起来,然后在适当的时候再获得.         序列化分为两大部分:序列化和反序列化.序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输.反序列

序列化与反序列化总结(Serializable和Parcelable)

序列化是指将对象的状态信息转换为可以存储或传输的形式的过程. 在Java中创建的对象,只要没有被回收就可以被复用,但是,创建的这些对象都是存在于JVM的堆内存中,JVM处于运行状态时候,这些对象可以复用, 但是一旦JVM停止,这些对象的状态也就丢失了. 在实际生活中,需要将对象持久化,需要的时候再重新读取出来,通过对象序列化,可以将对象的状态保存为字节数组,需要的时候再将字节数组反序列化为对象. 对象序列化可以很容易的在JVM中的活动对象和字节数组(流)之间转换,广泛用于RMI(远程方法调用)以

Java的序列化与反序列化

Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?本文围绕这些问题进行了探讨. 1.Java序列化与反序列化 Java序列化是指把Java对象转换为字节序列的过程:而Java反序列化是指把字节序列恢复为Java对象的过程. 2.为什么需要序列化与反序列化 我们知道,当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本.图片.音频.视频等, 而这些数据都会以二进制序列的形式在网络上传送.那么当两个Java进程进行通信时,能否实现进程间的对象传送

Python Special Syntax 8: 序列化与反序列化--&gt;华丽丽的叫 pickle(泡菜?!)

直接上代码吧 #-*-coding:utf-8 import os if os.path.exists('d:\\cpickle.data'): os.remove('d:\\cpickle.data') import cPickle as P shoplist=['apple','banana','pear'] P.dump(shoplist,file('d:\\cpickle.data','w')) f=file('d:\\cpickle.data') while True: content