http://www.codeproject.com/KB/cs/cloneimpl_class.aspx
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();
}
}
}
Now in the Main
method you can do the following:
static void Main(string[] args)
{
MyContainer con1 = new MyContainer();
MyContainer con2 = (MyContainer)con1.Clone();
con2.myArray[0].id = 5;
}
When inspecting the con2
instance you will see that the MyClass
instance in the first index was changed to 5, but the con1
instance remained without changes. So you can see that any field you will add to your class, which support the ICloneable
interface will be cloned as well. Furthermore, if the field supports the IList
interface or the IDictionary
interface, the method will detect it and will loop through all the items and will try to clone them as well.
Implementation
/// <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.
/// The class implements the interface ICloneable and there
/// for every object that will be derived
/// 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;
}
}
C#中的深复制和浅复制(在C#中克隆对象)
C# 支持两种类型:“值类型”和“引用类型”。
值类型(Value Type)(如 char、int 和 float)、枚举类型和结构类型。
引用类型(Reference Type) 包括类 (Class) 类型、接口类型、委托类型和数组类型。
如何来划分它们?
以它们在计算机内存中如何分配来划分
值类型与引用类型的区别?
1,值类型的变量直接包含其数据,
2,引用类型的变量则存储对象引用。
对于引用类型,两个变量可能引用同一个对象,因此对一个变量的操作可能影响另一个变量所引用的对象。对于值类型,每个变量都有自己的数据副本,对一个变量的操作不可能影响另一个变量。
值类型隐式继承自System.ValueType 所以不能显示让一个结构继承一个类,C#不支持多继承
堆栈(stack)是一种先进先出的数据结构,在内存中,变量会被分配在堆栈上来进行操作。
堆(heap)是用于为类型实例(对象)分配空间的内存区域,在堆上创建一个对象,
会将对象的地址传给堆栈上的变量(反过来叫变量指向此对象,或者变量引用此对象)。
关于对象克隆的所设计到知识点
浅拷贝:是指将对象中的所有字段逐字复杂到一个新对象
对值类型字段只是简单的拷贝一个副本到目标对象,改变目标对象中值类型字段的值不会反映到原始对象中,因为拷贝的是副本
对引用型字段则是指拷贝他的一个引用到目标对象。改变目标对象中引用类型字段的值它将反映到原始对象中,因为拷贝的是指向堆是上的一个地址
深拷贝:深拷贝与浅拷贝不同的是对于引用字段的处理,深拷贝将会在新对象中创建一个新的对象和
原始对象中对应字段相同(内容相同)的字段,也就是说这个引用和原始对象的引用是不同, 我们改变新
对象中这个字段的时候是不会影响到原始对象中对应字段的内容。
浅复制: 实现浅复制需要使用Object类的MemberwiseClone方法用于创建一个浅表副本
深复制: 须实现 ICloneable接口中的Clone方法,且需要需要克隆的对象加上[Serializable]特性
class DrawBase:System.Object , ICloneable
{
public string name = "jmj";
public DrawBase()
{
}
public object Clone()
{
return this as object; //引用同一个对象
return this.MemberwiseClone(); //浅复制
return new DrawBase() as object;//深复制
}
}
class Program
{
static void Main(string[] args)
{
DrawBase rect = new DrawBase();
Console.WriteLine(rect.name);
DrawBase line = rect.Clone() as DrawBase;
line.name = "a9fs3";
Console.WriteLine(rect.name);
DrawBase ploy = line.Clone() as DrawBase;
ploy.name = "lj";
Console.WriteLine(rect.name);
Console.WriteLine(object.ReferenceEquals(line, ploy));
Console.ReadLine();
}
}
运行结果:
return this as object; //引用同一个对象
输出:jmj
a9fs3
lj
True
return this.MemberwiseClone(); //浅复制
return new DrawBase() as object;//深复制
输出均为: jmj
jmj
jmj
False
解释:
return this as object 方法总是引用同一个对象,因此相应的堆内存上的值会改变!
后两种方法都是对对象的复制,区别在于复制的类别不同:深复制会复制整个填充的对象,包括该对象中其他引用类型和值类型的值;而浅复制只复制了一个对象中所有引用,它没有值的复制,通过引用它们的其他对象的引用来共享它们。