话谈c#拷贝

c#中类型分为值类型和引用类型,值类型对象赋值是本身就是赋的自身的一个副本,而引用类型赋值时则赋的是指向堆上的内存,假如我们不想赋这个地址而想将对象赋过去要怎么做呢?首先要知道拷贝分为浅表拷贝和深层拷贝,浅表拷贝得到一个新的实例,一个与原始对象类型相同、值类型字段相同的拷贝。但是,如果字段是引用类型的,则拷贝的是该引用, 而不是的对象。若想将引用字段的对象也拷贝过去,则称为深拷贝。 为了实现拷贝,本文总结了以下几种方法。

1.首先就是最笨的方法,传说中的“人工拷贝”,就是将引用里的所有值对象和具有值特征的string对象一一赋给新对象,这种方式代码量过大而且维护起来相当麻烦,所以能不用就不用。

2.System.Object提供了受保护的方法 MemberwiseClone,可用来实现“浅表”拷贝。由于该方法标记为“受保护”级别,因此,我们只能在继承类或该类内部才能访问该方法。

 public class A
{
public string rr { get; set; }
public string tt { get; set; } public A ShallowCopy()
{
return (A)this.MemberwiseClone();
} }

3.使用序列化与反序列化的方式,这种方式虽可实现深度拷贝,但有点大炮打蚊子的味道,而且在外面引用时一定要记得关闭所创建的MemoryStream流

 public static object Clone(object o,out MemoryStream ms)
{
BinaryFormatter bf = new BinaryFormatter(); ms = new MemoryStream();
bf.Serialize(ms,o);
ms.Seek(0, SeekOrigin.Begin); return bf.Deserialize(ms);
}

4.在一个外国人写的博客中(http://www.codeproject.com/Articles/3441/Base-class-for-cloning-an-object-in-C),使用反射的方法来解决了这个问题。他写了一个BaseObject类,如果我们继承这个类就可以实现深度拷贝,下面是他的实现方法:

创建一个实现 ICloneable 接口的有默认行为的抽象类,所谓的默认行为就是使用以下库函数来拷贝类里的每一个字段。

1.遍历类里的每个字段,看看是否支持ICloneable接口。

2.如果不支持ICloneable接口,按下面规则进行设置,也就是说如果字段是个值类型,那将其拷贝,如果是引用类型则拷贝字段指向通一个对象。

3.如果支持ICloneable,在科隆对象中使用它的克隆方法进行设置。

4.如果字段支持IEnumerable接口,需要看看它是否支持IList或者IDirectionary接口,如果支持,迭代集合看看是否支持ICloneable接口。

我们所要做的就是使得所有字段支持ICloneable接口。

下面是测试结果:

public class MyClass : BaseObject
{
public string myStr =”test”;
public int id;
} public class MyContainer : BaseObject
{
public string name = “test2”;
public MyClass[] myArray= new MyClass[5]; public class MyContainer()
{
for(int i=0 ; i<5 ; i++)
{
this.myArray[I] = new MyClass();
}
}
}
static void Main(string[] args)
{
MyContainer con1 = new MyContainer();
MyContainer con2 = (MyContainer)con1.Clone(); con2.myArray[0].id = 5;
}

con2中MyClass实例中第一个元素变成了5,但con1没有改变,即实现了深拷贝。

BaseObject的实现:

/// <summary>
/// BaseObject class is an abstract class for you to derive from.
/// Every class that will be dirived from this class will support the
/// Clone method automaticly.<br>
/// The class implements the interface ICloneable and there
/// for every object that will be derived <br>
/// from this object will support the ICloneable interface as well.
/// </summary> public abstract class BaseObject : ICloneable
{
/// <summary>
/// Clone the object, and returning a reference to a cloned object.
/// </summary>
/// <returns>Reference to the new cloned
/// object.</returns>
public object Clone()
{
//First we create an instance of this specific type.
object newObject = Activator.CreateInstance( this.GetType() ); //We get the array of fields for the new type instance.
FieldInfo[] fields = newObject.GetType().GetFields(); int i = 0; foreach( FieldInfo fi in this.GetType().GetFields() )
{
//We query if the fiels support the ICloneable interface.
Type ICloneType = fi.FieldType.
GetInterface( "ICloneable" , true ); if( ICloneType != null )
{
//Getting the ICloneable interface from the object.
ICloneable IClone = (ICloneable)fi.GetValue(this); //We use the clone method to set the new value to the field.
fields[i].SetValue( newObject , IClone.Clone() );
}
else
{
// If the field doesn't support the ICloneable
// interface then just set it.
fields[i].SetValue( newObject , fi.GetValue(this) );
} //Now we check if the object support the
//IEnumerable interface, so if it does
//we need to enumerate all its items and check if
//they support the ICloneable interface.
Type IEnumerableType = fi.FieldType.GetInterface
( "IEnumerable" , true );
if( IEnumerableType != null )
{
//Get the IEnumerable interface from the field.
IEnumerable IEnum = (IEnumerable)fi.GetValue(this); //This version support the IList and the
//IDictionary interfaces to iterate on collections.
Type IListType = fields[i].FieldType.GetInterface
( "IList" , true );
Type IDicType = fields[i].FieldType.GetInterface
( "IDictionary" , true ); int j = 0;
if( IListType != null )
{
//Getting the IList interface.
IList list = (IList)fields[i].GetValue(newObject); foreach( object obj in IEnum )
{
//Checking to see if the current item
//support the ICloneable interface.
ICloneType = obj.GetType().
GetInterface( "ICloneable" , true ); if( ICloneType != null )
{
//If it does support the ICloneable interface,
//we use it to set the clone of
//the object in the list.
ICloneable clone = (ICloneable)obj; list[j] = clone.Clone();
} //NOTE: If the item in the list is not
//support the ICloneable interface then in the
//cloned list this item will be the same
//item as in the original list
//(as long as this type is a reference type). j++;
}
}
else if( IDicType != null )
{
//Getting the dictionary interface.
IDictionary dic = (IDictionary)fields[i].
GetValue(newObject);
j = 0; foreach( DictionaryEntry de in IEnum )
{
//Checking to see if the item
//support the ICloneable interface.
ICloneType = de.Value.GetType().
GetInterface( "ICloneable" , true ); if( ICloneType != null )
{
ICloneable clone = (ICloneable)de.Value; dic[de.Key] = clone.Clone();
}
j++;
}
}
}
i++;
}
return newObject;
}
}
浮躁的人容易问:我到底该学什么;----别问,学就对了; 浮躁的人容易问:JS有钱途吗;----建议你去抢银行; 浮躁的人容易说:我要中文版!我英文不行!----不行?学呀! 浮躁的人分两种:只观望而不学的人;只学而不坚持的人; 浮躁的人永远不是一个高手。
 
分类: C#

c#拷贝的更多相关文章

  1. javascript中的继承与深度拷贝

    前言 本篇适合前端新人,下面开始...... 对于前端新手来说(比如博主),每当对js的对象做操作时,都是一种痛苦,原因就是在于对象的赋值是引用的传递,并非值的传递,虽然看上去后者赋值给了前者,他们就 ...

  2. C++ 拷贝构造函数和赋值运算符

    本文主要介绍了拷贝构造函数和赋值运算符的区别,以及在什么时候调用拷贝构造函数.什么情况下调用赋值运算符.最后,简单的分析了下深拷贝和浅拷贝的问题. 拷贝构造函数和赋值运算符 在默认情况下(用户没有定义 ...

  3. python的拷贝(深拷贝和浅拷贝)

    今天看了几篇关于python拷贝的博文,感觉不太清楚,所以我就自己做实验试一下,特此记录. 拷贝是针对组合对象说的,比如列表,类等,而数字,字符串这样的变量是没有拷贝这一说的. 实现拷贝有: 1.工厂 ...

  4. 实验:Oracle直接拷贝物理存储文件迁移

    实验目的:Oracle直接拷贝物理文件迁移,生产库有类似施工需求,故在实验环境简单验证一下. 实验环境: A主机:192.168.1.200 Solaris10 + Oracle 11.2.0.1 B ...

  5. Marshal.Copy将指针拷贝给数组

    lpStatuss是一个UNITSTATUS*的指针类型实例,并包含SensorDust字段 //定义一个数组类型 byte[] SensorDust = new byte[30] //将指针类型拷贝 ...

  6. C# 拷贝指定文件夹下的所有文件及其文件夹到指定目录

    要拷贝的文件及其文件夹结构 其中.lab文件不能覆盖 /// <summary> /// 拷贝oldlab的文件到newlab下面 /// </summary> /// < ...

  7. Linux下,拷贝文件时,排除某些文件

     一下是自己用到到几次实践,觉得很赞:   1.拷贝文件时,排除某些不需要的文件:   1)使用xargs来做: ls /tmp/test/ |grep -v .gz |xargs -i cp -r ...

  8. [LeetCode] Copy List with Random Pointer 拷贝带有随机指针的链表

    A linked list is given such that each node contains an additional random pointer which could point t ...

  9. 分享公司Entity与DTO之间数据拷贝的方法

    主题 最早以前自学java web的时候,数据库查询出来一个Entity对象(CMP对象).就直接传给前台展示了.并没有用到DTO对象,开始并没有觉得有什么不好...后来发现还是需要一些DTO对象来专 ...

  10. c++拷贝构造和编译优化

    #include <iostream> using namespace std; class MyClass { public: MyClass(); MyClass(int i); My ...

随机推荐

  1. Linux经常使用的命令-权利管理命令-权利管理命令chmod

    指令名字:chmod 命令英语的意图:change the permissions mode of a file 凡路径命令:/bin/chmod 语法:chmod [{ugoa}{+-=}{rwx} ...

  2. thinkphp学习笔记2—入口文件

    原文:thinkphp学习笔记2-入口文件 在thinkphp中有两个入口文件,一个是项目的入口文件,是index.php在主目录里面,还有一个是thinkphp框架的的入口文件,放在框架目录下面如: ...

  3. 十天学Linux内核之第一天---内核探索工具类

    原文:十天学Linux内核之第一天---内核探索工具类 寒假闲下来了,可以尽情的做自己喜欢的事情,专心待在实验室里燥起来了,因为大二的时候接触过Linux,只是关于内核方面确实是不好懂,所以十天的时间 ...

  4. HDU 4901 The Romantic Hero(二维dp)

    题目大意:给你n个数字,然后分成两份,前边的一份里面的元素进行异或,后面的一份里面的元素进行与.分的时候依照给的先后数序取数,后面的里面的全部的元素的下标一定比前面的大.问你有多上种放元素的方法能够使 ...

  5. 第4章 建造者模式(Builder Pattern)

    原文 第4章 建造者模式(Builder Pattern) 定义 将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式. 实用范围 1 当创建复杂对象 ...

  6. 第2章 简单工厂模式(Sample Factory)

    原文 第2章 简单工厂模式(Sample Factory) 一般用到的场景:对象多次被实例引用,切有可能会发生变化 拿我们的简单三层举例子 先定义dal层 1 2 3 4 5 6 7 8     cl ...

  7. C语言中符号格式说明

    scanf 语法: #include <stdio.h>int scanf( const char *format, ... ); scanf()函数根据由format(格式)指定的格式从 ...

  8. crawler_爬虫开发的曲线图

    个人总结爬虫的学习曲线,可分为三个阶段, 一. 主要在填充基础知识,要熟悉http协议,学习正则表达式,首先基于jdk的基础包的网络功能,.net包下的  httpurlconnction 从细节上简 ...

  9. MySql创建一个存储过程

    MySQL 存储过程是从 MySQL 5.0 新功能.存储过程的长处有一箩筐.只是最基本的还是运行效率和SQL 代码封装. 特别是 SQL 代码封装功能,假设没有存储过程,在外部程序訪问数据库时(比如 ...

  10. Ubuntu下怎样切换到ROOT登录

    原文:http://james23dier.iteye.com/blog/721246 近期一直在学习linux,选择ubuntu作为联系的操作系统.然后一直发现自己所创建的用户和root用户不是一个 ...