临时解压缩 - 这是IDisposable接口的有效使用吗?

时间:2016-01-07 12:39:20

标签: c# .net idisposable

我想封装解压缩zip文件的过程,使文件可供使用,然后在不再需要时自动清理它们。我用一个实现IDisposable接口的类来做这个,这样我就可以用"使用"来实例化它。当超出范围时,文件将被清除,无需专门删除文件。因此可以使用类TempUnzip

    static void AccessZipFileContents(string zipFilePath)
    {
        using (var temp = new TempUnzip(zipFilePath)
        {
            var tempPath = temp.TempPath;
            if (tempPath != null)
            {
                // read/use the files in tempPath
            }
        } // files automatically get deleted when it goes out of scope! Woohoo!
    }

以下是TempUnzip类的实现:

using System.IO;
using System.IO.Compression;
public class TempUnzip : IDisposable
{
    public TempUnzip(string zipFilePath)
    {
        try
        {
            var tempFolderName = Path.GetRandomFileName();
            var tempFolder = Path.GetTempPath();
            var tempPath = Path.Combine(tempFolder, tempFolderName);
            Directory.CreateDirectory(tempPath);
            ZipFile.ExtractToDirectory(zipFilePath, tempPath);
            TempPath = tempPath;
        }
        catch (Exception) { TempPath = null; }
    }

    public readonly string TempPath;

    public void Dispose()
    {
        try
        {
            if (TempPath != null)
                Directory.Delete(TempPath);
        }
        catch (Exception) { }
    }
}

这是IDisposable的有效用途吗?

  • 如果是,我是否需要实施the full standard IDisposable pattern?

  • 如果没有,是否有更好的方法来封装文件的创建和销毁,使其与对象的生命周期相关联,或者我应该完全避免这种情况?

3 个答案:

答案 0 :(得分:3)

这是IDisposable的有效用途吗?

来自documentation

  

提供释放非托管资源的机制。

本地磁盘上的文件肯定是非托管资源。因此,这种用法符合IDisposable的陈述目的。

如果是,我是否需要实施完整的标准IDisposable模式?

你可以。关于终结器的常见警告需要考虑,但您已经与这些相关联。它当然不会受到伤害。

如果没有,是否有更好的方法来封装文件的创建和销毁,使其与对象的生命周期相关联,或者我应该完全避免这种情况? < / p>

我也喜欢这种问题的功能性方法。这会使你的例子看起来像这样:

static void AccessZipFileContents(string zipFilePath)
{
    ZipManager.RunWithZipExtracted(zipFilePath, (string tempPath) =>
    {
        if (tempPath != null)
        {
            // read/use the files in tempPath
        }
    } // files automatically get deleted when it goes out of scope! Woohoo!
}

//from ZipManager.cs...
static void RunWithZipExtracted(string zipLocation, Action<string> toRun)
{
    var tempPath = CalculateTempPath();
    try
    {
        ExtractZip(zipLocation, tempPath);
        toRun(tempPath);
    }
    finally
    {
        DeleteFolder(tempPath);
    }
 } //private methods left as exercise for the reader

这样的模式避免了“如果他们不调用Dispose会怎么样?”的问题。完全。

答案 1 :(得分:1)

这种情况下,终结器可能是一个好主意,但MS模式是基于具有终结器的 public 对象的想法,这几乎总是一个坏主意。相反,需要基于最终化清理的资源应该封装在私有对象中,这些对象的引用永远不会暴露给外部世界。因为内部对象是私有的,所以他们不需要使用IDisposable模式 - 相反,它们可以以最适合要求的任何方式设计。

由于尝试多次关闭文件句柄可能会产生灾难性后果,并且在其他代码使用对象(甚至执行Dispose时,终结器可能会执行(尽管很少见)在它上面!),写一个健壮的课程可能很困难。出现的一个令人讨厌的问题是文件访问可以阻止,但终结器操作不应该。可以通过创建一个线程来解决这个问题,该线程的目的是等待文件被关闭和删除,然后关闭并删除文件。即使删除文件的尝试被阻止,其他终结器操作也可能继续快速进行。不幸的是,为了允许安全的基于终结器的清理而急切地创建一个线程很容易浪费资源,但是线程创建似乎是一个在终结器中执行的过于重量级的任务。我不知道最好的解决方案是什么。

答案 2 :(得分:-1)

在这种情况下,我会说它是IDisposable的一个很好的例子,如果Dispose没有被立即调用,你已经完成了使用它,那么它不是世界末日和为同一个zip文件快速调用此文件不会导致异常,因为您每次都使用唯一的临时文件夹;再次,你不会让文件指针在文件上保持打开等等。我唯一能看到的问题是,如果磁盘空间非常紧张,你可能希望将delete文件夹公开为允许选项的方法直接调用,然后Dispose只用于清理所有情况。

注意:在这个例子中你可能想要调用Directory.Delete(TempPath,true);因为你调用的方法会抛出IOException,如果文件夹不是空的(你的捕获将会隐藏的东西) - 请参阅https://msdn.microsoft.com/en-us/library/62t64db3%28v=vs.110%29.aspx