当前位置:首页 > C#编程 > C#/.net框架 > 正文内容

C#实现对象的深度复制,可以用于解决文件被占用的问题

Jorge2年前 (2022-05-09)C#/.net框架654

 

在解决“文件被占用”的问题时,有时候除了即时释放资源句柄,还需要使用对象的深度复制,这样就相当于斩断了原对象的占用。


==========正文开始===================

C#
MemberwiseClone 方法创建一个浅表副本,具体来说就是创建一个新对象,
然后将当前对象的非静态字段复制到该新对象。
如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,
则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。

为了实现深度复制,我们就必须遍历有相互引用的对象构成的图,并需要处理其中的循环引用结构。这无疑是十分复杂的。幸好借助.Net的序列化和反序 列化机制,可以十分简单的深度Clone一个对象。原理很简单,首先将对象序列化到内存流中,此时对象和对象引用的所用对象的状态都被保存到内存 中。.Net的序列化机制会自动处理循环引用的情况。然后将内存流中的状态信息反序列化到一个新的对象中。这样一个对象的深度复制就完成了。在原型设计模 式中CLONE技术非常关键。

下面的代码就是演示这个问题:

C#
using System;using System.IO;using System.Runtime.Serialization.Formatters.Binary;namespace CloneDemo{[Serializable]class DemoClass{
    public int i = 0;
    public int[] iArr = { 1, 2, 3 };
 
    public DemoClass Clone1() //浅CLONE
    {
        return this.MemberwiseClone() as DemoClass;
    }
 
    public DemoClass Clone2() //深clone
    {
        MemoryStream stream = new MemoryStream();
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, this);
        stream.Position = 0;
        return formatter.Deserialize(stream) as DemoClass;
    }}
 class Program{
    static void Main(string[] args)
    {
        DemoClass a = new DemoClass();
        a.i = 10;
        a.iArr = new int[] { 8, 9, 10 };
        DemoClass b = a.Clone1();
        DemoClass c = a.Clone2();
 
        // 更改 a 对象的iArr[0], 导致 b 对象的iArr[0] 也发生了变化 而 c不会变化
          a.iArr[0] = 88;
 
        Console.WriteLine("MemberwiseClone");
        Console.WriteLine(b.i);
        foreach (var item in b.iArr)
        {
            Console.WriteLine(item);
        }
 
        Console.WriteLine("Clone2");
        Console.WriteLine(c.i);
        foreach (var item in c.iArr)
        {
            Console.WriteLine(item);
        }
 
        Console.ReadLine();
    }}}

另外一个例子是针对数组,C#中的数组是引用型的变量,我们通过数组来进行演示:
浅拷贝

C#
using System;class ShallowCopy : ICloneable{public int[] v = {1,2,3};public Object Clone(){return this.MemberwiseClone();}public void Display(){foreach(int i in v)Console.Write( i + ", ");Console.WriteLine();}}class Client{public static void Main(){ShallowCopy sc1 = new ShallowCopy();ShallowCopy sc2 = (ShallowCopy)sc1.Clone();sc1.v[0] = 9;
 sc1.Display();sc2.Display();}}

ShallowCopy对象实现了一个浅拷贝,因此当对sc1进行克隆时,其字段v并没有克隆,这导致sc1与sc2的字段v都指向了同一个v,因此,当修改了sc1的v[0]后,sc2的v[0]也发生了变化。

深拷贝:

C#
using System;class DeepCopy : ICloneable{public int[] v = {1,2,3};// 默认构造函数public DeepCopy(){}// 供Clone方法调用的私有构造函数private DeepCopy(int[] v){this.v = (int[])v.Clone();}public Object Clone(){// 构造一个新的DeepCopy对象,构造参数为// 原有对象中使用的 vreturn new DeepCopy(this.v);}public void Display(){foreach(int i in v)Console.Write( i + ", ");Console.WriteLine();}}class Client{public static void Main(){DeepCopy dc1 = new DeepCopy();DeepCopy dc2 = (DeepCopy)dc1.Clone();dc1.v[0] = 9;
 dc1.Display();dc2.Display();}}

这次在克隆的时候,不但克隆对象本身,连里面的数组字段一并克隆。因此,最终打印出来的dc1与dc2不同。

当然我们也可以建个深拷贝的帮助类:

C#
public static class ObjectCopier{
    /// <summary>/// Perform a deep Copy of the object./// </summary>/// <typeparam name="T">The type of object being copied.</typeparam>/// <param name="source">The object instance to copy.</param>/// <returns>The copied object.</returns>public static T Clone<T>(T source){
    if (!typeof(T).IsSerializable)
    {
        throw new ArgumentException("The type must be serializable.", "source");
    }
 
    // Don't serialize a null object, simply return the default for that object
    if (Object.ReferenceEquals(source, null))
    {
        return default(T);
    }
 
    IFormatter formatter = new BinaryFormatter();
    Stream stream = new MemoryStream();
    using (stream)
    {
        formatter.Serialize(stream, source);
        stream.Seek(0, SeekOrigin.Begin);
        return (T)formatter.Deserialize(stream);
    }}}


使用的例子:

C#
 var f = root.GetFiles();
 ....
 f = ObjectCopier.Clone<List<FileInfo>>(tmp).ToArray();

这个例子中,GetFiles取得目录的图片,然后会将它关联到listview控件的大图标上去。

然后会对f中的内容进行一些排序,当再次将它关联到listview上去时,会报“文件被占用”。

解决方法就是使用f前,把它的排序后的内容进行深度复制后再赋值回来。





--------------------- 

作者:hackpig

来源:www.skcircle.com

版权声明:本文为博主原创文章,转载请附上博文链接!


扫描二维码推送至手机访问。

版权声明:本文由7点博客发布,如需转载请注明出处。

本文链接:http://6dot.cn/?id=97

标签: .NET.NET框架
分享给朋友:

“C#实现对象的深度复制,可以用于解决文件被占用的问题” 的相关文章

C#的变迁史 - C# 4.0 之线程安全集合篇

C#的变迁史 - C# 4.0 之线程安全集合篇

作为多线程和并行计算不得不考虑的问题就是临界资源的访问问题,解决临界资源的访问通常是加锁或者是使用信号量,这个大家应该很熟悉了。  而集合作为一种重要的临界资源,通用性更广,为了让大家更安全的使用它们,微软为我们带来了强大的并行集合:System.Collections.Concurrent里面的各...

C# tableLayoutPanel动态加载控件闪烁的解决方案

C# tableLayoutPanel动态加载控件闪烁的解决方案

WinForm加载多个自定义控件时,会出现很严重的闪烁问题,很卡,一块一块的加载(像打开网页时,网络很卡的那种感觉)简直没法忍受。在网上搜索了好久,网上大部分的方法是一下4种,但是都不能有效的解决问题。  1.将DoubleBuffered 设置 true,用双缓存处理Form界面内容加载,可以提高...

C# 外观模式(Facade)举一个例子

C# 外观模式(Facade)举一个例子

 有关外观模式见下面贴子,下面编写的一个例子。FundClass.csC#using System;using System.Collections.Generic;using System.Linq;using System.Text;namesp...

C# winform的MVC设计模式

C# winform的MVC设计模式

 MVC模式主要解决的问题就是将表示层和业务层进行分离,在以往做WINFORM项目的时候,通常都是将很多的逻辑代码直接写在了Form.cs代码的事件里,这样的话业务逻辑就和界面紧耦合在一起了,现在我们采用MVC来解耦。首先建立Model:C#[csharp] view ...

C# Task.Result与Task.GetAwaiter.GetResult()区别

C# Task.Result与Task.GetAwaiter.GetResult()区别

前几天在用线程池执行一些任务时运到一种情形,就是回调方法中使用到了异步方法,但是回调方法貌似不支持async await的写法。这时候我应该如何处理呢?是使用Task.Result来获取返回结果,还是使用GetAwaiter.GetResult()呢?本文就来探讨下吧。这里先上我这种场景的伪代码:T...

C# 事件总线 EventBus

C# 事件总线 EventBus

 1. 引言事件总线这个概念对你来说可能很陌生,但提到观察者(发布-订阅)模式,你也许就很熟悉。事件总线是对发布-订阅模式的一种实现。它是一种集中式事件处理机制,允许不同的组件之间进行彼此通信而又不需要相互依赖,达到一种解耦的目的。我们来看看事件总线的处理流程:了解了事件总线的基本概念和处...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。