摘要
该接口使你能够创建现有对象的副本的自定义的实现。该接口只提供了,一个Clone方法,实现对象的浅拷贝。有浅拷贝,那么就有相对应的深拷贝。但该接口并没有对我们提供,需要我们自己实现。
什么是浅拷贝与深拷贝?
浅拷贝
将对象的字段复制到新的对象副本中,同时将字段的值也复制过去,但引用类型值复制引用,而不是引用类型本身,也就是,如果源对象的引用类型的字段的值改变了,拷贝的对象的对应的引用类型的字段也会跟着变化。
深拷贝
将对象的字段复制到新的对象副本中,无论是值类型还是引用类型的字段,都会复制类型本身及值。但,源对象的值变化,并不会影响副本中的对应的值。
一个例子
浅拷贝
<span style="color: #0000ff;">namespace<span style="color: #000000;"> Wolfy.CloneDemo
{
<span style="color: #0000ff;">class<span style="color: #000000;"> Program
{
<span style="color: #0000ff;">static <span style="color: #0000ff;">void Main(<span style="color: #0000ff;">string<span style="color: #000000;">[] args)
{
Console.WriteLine(<span style="color: #800000;">"<span style="color: #800000;">创建新对象:<span style="color: #800000;">"<span style="color: #000000;">);
Person p = <span style="color: #0000ff;">new Person() { Name = <span style="color: #800000;">"<span style="color: #800000;">wolfy<span style="color: #800000;">",Id = <span style="color: #800080;">1,Address = <span style="color: #0000ff;">new Address { City = <span style="color: #800000;">"<span style="color: #800000;">北京<span style="color: #800000;">",Details = <span style="color: #800000;">"<span style="color: #800000;">北京 海淀区<span style="color: #800000;">"<span style="color: #000000;"> } };
Console.WriteLine(p.ToString());
Console.WriteLine(<span style="color: #800000;">"<span style="color: #800000;">克隆对象:<span style="color: #800000;">"<span style="color: #000000;">);
Person p2 =<span style="color: #000000;"> (Person)p.Clone();
Console.WriteLine(p2.ToString());
<span style="color: #0000ff;">var result = <span style="color: #0000ff;">object<span style="color: #000000;">.ReferenceEquals(p,p2);
Console.WriteLine(<span style="color: #800000;">"<span style="color: #800000;">p ReferenceEquals p2:<span style="color: #800000;">" +<span style="color: #000000;"> result);
Console.WriteLine(<span style="color: #800000;">"<span style="color: #800000;">此时修改p对象,是否会影响p2对象?<span style="color: #800000;">"<span style="color: #000000;">);
p.Name = <span style="color: #800000;">"<span style="color: #800000;">wolfy2<span style="color: #800000;">"<span style="color: #000000;">;
p.Id = <span style="color: #800080;">2<span style="color: #000000;">;
p.Address.City = <span style="color: #800000;">"<span style="color: #800000;">上海<span style="color: #800000;">"<span style="color: #000000;">;
Console.WriteLine(<span style="color: #800000;">"<span style="color: #800000;">p:<span style="color: #800000;">" +<span style="color: #000000;"> p.ToString());
Console.WriteLine(<span style="color: #800000;">"<span style="color: #800000;">p2:<span style="color: #800000;">" +<span style="color: #000000;"> p2.ToString());
{
<span style="color: #0000ff;">class<span style="color: #000000;"> Program
{
<span style="color: #0000ff;">static <span style="color: #0000ff;">void Main(<span style="color: #0000ff;">string<span style="color: #000000;">[] args)
{
Console.WriteLine(<span style="color: #800000;">"<span style="color: #800000;">创建新对象:<span style="color: #800000;">"<span style="color: #000000;">);
Person p = <span style="color: #0000ff;">new Person() { Name = <span style="color: #800000;">"<span style="color: #800000;">wolfy<span style="color: #800000;">",Id = <span style="color: #800080;">1,Address = <span style="color: #0000ff;">new Address { City = <span style="color: #800000;">"<span style="color: #800000;">北京<span style="color: #800000;">",Details = <span style="color: #800000;">"<span style="color: #800000;">北京 海淀区<span style="color: #800000;">"<span style="color: #000000;"> } };
Console.WriteLine(p.ToString());
Console.WriteLine(<span style="color: #800000;">"<span style="color: #800000;">克隆对象:<span style="color: #800000;">"<span style="color: #000000;">);
Person p2 =<span style="color: #000000;"> (Person)p.Clone();
Console.WriteLine(p2.ToString());
<span style="color: #0000ff;">var result = <span style="color: #0000ff;">object<span style="color: #000000;">.ReferenceEquals(p,p2);
Console.WriteLine(<span style="color: #800000;">"<span style="color: #800000;">p ReferenceEquals p2:<span style="color: #800000;">" +<span style="color: #000000;"> result);
Console.WriteLine(<span style="color: #800000;">"<span style="color: #800000;">此时修改p对象,是否会影响p2对象?<span style="color: #800000;">"<span style="color: #000000;">);
p.Name = <span style="color: #800000;">"<span style="color: #800000;">wolfy2<span style="color: #800000;">"<span style="color: #000000;">;
p.Id = <span style="color: #800080;">2<span style="color: #000000;">;
p.Address.City = <span style="color: #800000;">"<span style="color: #800000;">上海<span style="color: #800000;">"<span style="color: #000000;">;
Console.WriteLine(<span style="color: #800000;">"<span style="color: #800000;">p:<span style="color: #800000;">" +<span style="color: #000000;"> p.ToString());
Console.WriteLine(<span style="color: #800000;">"<span style="color: #800000;">p2:<span style="color: #800000;">" +<span style="color: #000000;"> p2.ToString());
Console.Read();
}
}
[Serializable]
</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Person : ICloneable
{
</span><span style="color: #808080;">///</span> <span style="color: #808080;"><summary></span>
<span style="color: #808080;">///</span><span style="color: #008000;"> 值类型
</span><span style="color: #808080;">///</span> <span style="color: #808080;"></summary></span>
<span style="color: #0000ff;">private</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> _id;
</span><span style="color: #808080;">///</span> <span style="color: #808080;"><summary></span>
<span style="color: #808080;">///</span><span style="color: #008000;"> 特殊的引用类型
</span><span style="color: #808080;">///</span> <span style="color: #808080;"></summary></span>
<span style="color: #0000ff;">private</span> <span style="color: #0000ff;">string</span><span style="color: #000000;"> _name;
</span><span style="color: #0000ff;">private</span><span style="color: #000000;"> Address _address;
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span> Id { <span style="color: #0000ff;">get</span> => _id; <span style="color: #0000ff;">set</span> => _id =<span style="color: #000000;"> value; }
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">string</span> Name { <span style="color: #0000ff;">get</span> => _name; <span style="color: #0000ff;">set</span> => _name =<span style="color: #000000;"> value; }
</span><span style="color: #0000ff;">public</span> Address Address { <span style="color: #0000ff;">get</span> => _address; <span style="color: #0000ff;">set</span> => _address =<span style="color: #000000;"> value; }
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">override</span> <span style="color: #0000ff;">string</span><span style="color: #000000;"> ToString()
{
</span><span style="color: #0000ff;">return</span> JsonConvert.SerializeObject(<span style="color: #0000ff;">this</span><span style="color: #000000;">);
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">object</span><span style="color: #000000;"> Clone()
{
</span><span style="color: #008000;">/*</span><span style="color: #008000;">
MemberwiseClone<a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>创建的新对象,然后将当前对象的非静态字段复制到新的<a href="https://www.jb51.cc/tag/duixiangchuangjian/" target="_blank" class="keywords">对象创建</a>的浅表副本。
如果字段是值类型,则执行字段的按位复制。 如果字段是引用类型,引用将复制,但被引用的对象不;
因此,原始对象和其克隆引用同一对象。
例如,考虑对象称为 X 引用对象 A 和 B,反过来,引用对象 c。
X 的浅表副本创建新的对象 X2 也引用对象 A 和 b。与此相反,
X 的深层副本创建新对象 X2 引用 A2 和 B2,
是的一个副本的新对象并 B.B2,
反过来,引用新对象 C2,这是 C 的副本。该示例说明浅和深层复制操作之间的差异。
有很多<a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>可以实现深层复制操作,如果浅表复制操作由MemberwiseClone<a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>并不满足你的需求。
这些要求<a href="https://www.jb51.cc/tag/baokuo/" target="_blank" class="keywords">包括</a>:
<a href="https://www.jb51.cc/tag/diaoyong/" target="_blank" class="keywords">调用</a>类构造<a href="https://www.jb51.cc/tag/hanshu/" target="_blank" class="keywords">函数</a>要复制可以使用来自第一个对象的<a href="https://www.jb51.cc/tag/shuxing/" target="_blank" class="keywords">属性</a>值创建第二个对象的对象。
这假定,对象的值完全由其类构造<a href="https://www.jb51.cc/tag/hanshu/" target="_blank" class="keywords">函数</a>中定义。
<a href="https://www.jb51.cc/tag/diaoyong/" target="_blank" class="keywords">调用</a>MemberwiseClone<a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>创建一个对象,
对象的浅表副本并将其值是与原始对象的任何<a href="https://www.jb51.cc/tag/shuxing/" target="_blank" class="keywords">属性</a>或其值是引用类型的字段相同的新对象。
DeepCopy<a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>在示例中演示了此<a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>。
序列化对象是较深复制,,然后将序列化的数据还原到另一个对象变量。
使用具有递归反射来执行深层复制操作。
</span><span style="color: #008000;">*/</span>
<span style="color: #0000ff;">return</span> <span style="color: #0000ff;">this</span><span style="color: #000000;">.MemberwiseClone();
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">object</span><span style="color: #000000;"> DeepClone()
{
</span><span style="color: #0000ff;">using</span> (Stream objectStream = <span style="color: #0000ff;">new</span><span style="color: #000000;"> MemoryStream())
{
IFormatter formatter </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> BinaryFormatter();
formatter.Serialize(objectStream,</span><span style="color: #0000ff;">this</span><span style="color: #000000;">);
objectStream.Seek(</span><span style="color: #800080;">0</span><span style="color: #000000;">,SeekOrigin.Begin);
</span><span style="color: #0000ff;">return</span> formatter.Deserialize(objectStream) <span style="color: #0000ff;">as</span><span style="color: #000000;"> Person;
}
}
}
[Serializable]
</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Address
{
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">string</span><span style="color: #000000;"> _city;
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">string</span><span style="color: #000000;"> _details;
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">string</span> City { <span style="color: #0000ff;">get</span> => _city; <span style="color: #0000ff;">set</span> => _city =<span style="color: #000000;"> value; }
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">string</span> Details { <span style="color: #0000ff;">get</span> => _details; <span style="color: #0000ff;">set</span> => _details =<span style="color: #000000;"> value; }
}
}
如上图所示,浅拷贝,值类型的并不会相互影响,但是引用类型的Address会跟着改变。浅拷贝,在对引用类型的字段时,会拷贝指向该对象的引用。
深拷贝
将
Person p2 = (Person)p.Clone();
改为
Person p2 = (Person)p.DeepClone();
结语
在实际项目中,这个接口自己是没用过。在看c#相关的文章的时候,看到了该接口的介绍,一篇文章了解下。如果非要说应用场景的话,比如,如果一个方法参数是一个引用类型,你需要在方法中对其进行操作,但又不想改变它原有的值,可以实现该接口,在clone实现深拷贝,对拷贝的副本进行操作,不影响它的原有的值,返回一个新的对象。
原文链接:https://www.f2er.com/csharp/191503.html