一、浅克隆和深克隆(浅复制和深复制)
浅克隆和深克隆最典型的应用是数据集对象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引发内存泄漏,这里是内存泄漏的例子。

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

  1. java浅克隆和深克隆,序列化和反序列化实现深克隆(封装序列化和反序列化操作)

    本篇博客内容: 一.浅克隆(ShallowClone)和深克隆(DeepClone) 二.序列化和反序列化实现深克隆 三.封装序列化和反序列化操作 ObjectOutputStream + 内存流By ...

  2. 【Java一看就懂】浅克隆和深克隆

    一.何为克隆 在Java的体系中,数据类型分为基本数据类型和引用数据类型. 基本数据类型包括byte,short,int,long,float,double,boolean,char 8种,其克隆可通 ...

  3. Java的浅克隆和深克隆

    如何实现对象的克隆 (1)实现 Cloneable 接口并重写 Object 类中的 clone() 方法: (2)实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真 ...

  4. Java对象的浅克隆和深克隆

    为什么需要克隆      在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能会需要一个和A完全相同新对象B, 并且此后对B任何改动都不会影响到A中的值 ...

  5. Java必备技能:clone浅克隆与深克隆

    介绍 一直以来只知道Java有clone方法,该方法属于Object的,对于什么是浅克隆与深克隆就比较模糊了,现在就来补充学习一下. 概念 浅拷贝(浅克隆)复制出来的对象的所有变量都含有与原来的对象相 ...

  6. java序列化和反序列化使用总结

    一.概念 java对象序列化的意思就是将对象的状态转化成字节流,以后可以通过这些值再生成相同状态的对象.对象序列化是对象持久化的一种实现方法,它是将对象的属性和方法转化为一种序列化的形式用于存储和传输 ...

  7. C# 序列化与反序列化几种格式的转换

    这里介绍了几种方式之间的序列化与反序列化之间的转换 首先介绍的如何序列化,将object对象序列化常见的两种方式即string和xml对象; 第一种将object转换为string对象,这种比较简单没 ...

  8. 使用Newtonsoft.Json.dll(JSON.NET)动态解析JSON、.net 的json的序列化与反序列化(一)

    在开发中,我非常喜欢动态语言和匿名对象带来的方便,JSON.NET具有动态序列化和反序列化任意JSON内容的能力,不必将它映射到具体的强类型对象,它可以处理不确定的类型(集合.字典.动态对象和匿名对象 ...

  9. Java 序列化与反序列化

    1.什么是序列化?为什么要序列化? Java 序列化就是指将对象转换为字节序列的过程,而反序列化则是只将字节序列转换成目标对象的过程. 我们都知道,在进行浏览器访问的时候,我们看到的文本.图片.音频. ...

随机推荐

  1. iOS学习12之OC属性和点语法

    1.属性(@property和@Synthesize) 1> 属性是 Objective-C 2.0 定义的语法,提供 setter 和 getter 方法的默认实现.在一定程度上简化代码,并且 ...

  2. BZOJ2652 : 三角板

    首先旋转坐标系,假设$(x,y)$被$(X,Y)$遮挡等价于$X\leq x$且$Y\leq y$. 对于每种坐标系建立两棵线段树: 第一棵按$x$维护已经加入的点的$y$的最小值: 第二棵按$x$维 ...

  3. BZOJ1114 : [POI2008]鲁滨逊逃生Rob

    设船最宽行列的交点为船的重心,那么只要预处理出重心在每个位置是否可行,以及在边界上走出边界所需的最小值之后,进行一遍BFS即可. 枚举每个点$(x,y)$,求出它上下最近的障碍物的距离.考虑重心在第$ ...

  4. c++实现单向链表

    一.问题描述 1.题目内容:集合的并.交和差运算 编写一个能演示执行集合的并.交和差运算的程序. 2.基本要求 由用户输入两组整数分别作为两个集合的元素,由程序计算它们的交.并和差集,并将运算结果输出 ...

  5. sql:找出工资第二高的人名

    CREATE TABLE EmpSalaryInfo ( Id ), Name ), Salary int ) ) ) ) ) 方法1 (子查询): name from test where sala ...

  6. BZOJ3636: 教义问答手册

    Description “汉中沃野如关中,四五百里烟蒙蒙.黄云连天夏麦熟,水稻漠漠吹秋风.”——摘自 黄裳<汉中行>“泉岭精神不朽,汉中诸球永生.”——摘自<泉岭精神创立者语录> ...

  7. MongoDB 用户配置

    ====[安装]====DOS下切换到文件所在盘符 例如 D:\MongoDB\bin设置数据库保存位置 mongod.exe --dbpath D:\MongoDB\Data [--auth]//用 ...

  8. UpdatePanel完成后调用js

    引言: asp.net 微软引入了UpdatePanel 使用起来很方便 如果 我们想UpdatePanel加载完成后做一些事情 需要使用js <script type="text/j ...

  9. PHP 下的SSL加密设置

    这个是报的错[Composer\Downloader\TransportException] The . OpenSSL Error messages: error::SSL routines:SSL ...

  10. 使用diff制作补丁

    1.制作补丁包 命令格式 diff -uNr  oldfile.c newfile.c > x.patch 2.打补丁 命令格式 patch -p0 < x.patch 总结一下:单个文件 ...