C#使用IDisposable清理临时文件

时间:2010-11-05 11:19:26

标签: c# interface idisposable

我有一个FileUploader类,可以选择为其提供一个zip文件,然后将其提取到临时位置并返回文件路径。

根据我的理解,在FileUploader类上实现IDisposable接口并使用Dispose方法删除所有临时文件将使该类在其引用脱离上下文后立即自行清理?

但事实似乎并非如此 - 任何人都可以解释我如何去实现我想要实现的目标吗?

更新
为了澄清,我的代码是:

    public ActionResult ImportFile()
    {
        FileUploader uploader = new FileUploader(ControllerContext, "file"); // where "file" is the posted form's file element

        uploader.SaveOrExtractFilesToTempLocation();

        foreach (string file in uploader.files)
        {
            try
            {
                 // do some stuff
            }
            catch (Exception err)
            {
                 // let the user know
            }
        }
        return View();
    }

我试图在ImportFile()方法完成后让FileUploader删除所有临时文件

7 个答案:

答案 0 :(得分:7)

“脱离背景”尚不清楚;你必须这样做:

using(var file = new FileUploader(...)) {
    // do the work here
}

没有using,没有特殊处理。然后编译器将其转换为:

var file = new FileUploader(...);
IDisposable tmp = file;
try {
    // do the work here
} finally {
    if(tmp != null) tmp.Dispose();
}

并且会导致确定性清理。

答案 1 :(得分:7)

您需要正确实现IDisposable。喜欢下面的课程。

using System.IO;

/// <summary>
/// Represents a temporary storage on file system.
/// </summary>
public sealed partial class TempStorage
     : IDisposable
{
    #region Constructor

    private TempStorage()
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="TempStorage"/> class.
    /// </summary>
    /// <param name="path">The path to use as temp storage.</param>
    public TempStorage(string path)
    {
        this.Path = path;
        this.Clear();
        this.Create();
    }

    #endregion

    #region Properties

    private string Path
    {
        get;
        set;
    }

    #endregion

    #region Methods

    private void Create()
    {
        try
        {
            if (!Directory.Exists(this.Path))
            {
                Directory.CreateDirectory(this.Path);
            }
        }
        catch (IOException)
        {
        }
    }

    public void Clear()
    {
        try
        {
            if (Directory.Exists(this.Path))
            {
                Directory.Delete(this.Path, true);
            }
        }
        catch (IOException)
        {
        }
    }

    #endregion

    #region IDisposable

    /// <summary>
    /// An indicator whether this object is beeing actively disposed or not.
    /// </summary>
    private bool disposed;

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// Throws an exception if something is tried to be done with an already disposed object.
    /// </summary>
    /// <remarks>
    /// All public methods of the class must first call this.
    /// </remarks>
    public void ThrowIfDisposed()
    {
        if (this.disposed)
        {
            throw new ObjectDisposedException(this.GetType().Name);
        }
    }

    /// <summary>
    /// Releases managed resources upon dispose.
    /// </summary>
    /// <remarks>
    /// All managed resources must be released in this
    /// method, so after disposing this object no other
    /// object is beeing referenced by it anymore.
    /// </remarks>
    private void ReleaseManagedResources()
    {
        this.Clear();
    }

    /// <summary>
    /// Releases unmanaged resources upon dispose.
    /// </summary>
    /// <remarks>
    /// All unmanaged resources must be released in this
    /// method, so after disposing this object no other
    /// object is beeing referenced by it anymore.
    /// </remarks>
    private void ReleaseUnmanagedResources()
    {
    }

    private void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            /* Release unmanaged ressources */
            this.ReleaseUnmanagedResources();

            if (disposing)
            {
                /* Release managed ressources */
                this.ReleaseManagedResources();
            }

            /* Set indicator that this object is disposed */
            this.disposed = true;
        }
    }

    #endregion
}

然后在main方法中使用using-block中的类,如下所示:

using(TempStorage myStorage = new TempStorage("C:\temp")
{
    // rest of the main method here...
}

答案 2 :(得分:2)

只有使用Dispose关键字包装上下文时,

using才会自动生成:

using (FileUploader uploader = new FileUploader(...))
    uploader.UploadFiles();

答案 3 :(得分:1)

你可以实现终结器,这样如果你忘记拨打Dispose(你真的不应该!)并在你的终结器中调用Dispose。当对象被垃圾收集时会调用它 - 但它是不确定的,即没有办法找出它何时被调用。

无法保证

答案 4 :(得分:0)

正如其他人已经说过的那样,除非上下文包含Dispose关键字,否则不会自动调用using。但是,您可以实现Dispose和Finalize模式(从终结器/析构函数调用Dispose方法)。这样,即使您的代码没有直接调用Dispose,您也可以使用故障保护并删除临时文件(因为垃圾收集器最终会调用终结器)。

This article说明了这一概念,并提供了有关最终确定如何运作的一些见解。

答案 5 :(得分:0)

与其他人一样,最好的办法是将其包装在using语句中以强制调用Dispose方法。我相信这就是微软推荐使用任何实现IDispose的方法,至少基于他们的代码分析规则。

答案 6 :(得分:0)

IDisposable适用于对象以某种方式在对象消失之前清除的方式更改外部的内容。临时文件的创建似乎符合条件。但是,有一些警告:

  1. 不幸的是,处理程序在运行时确定异常是否未决是没有好方法的。如果我有我的druthers,会有一个IDisposeableEx接口,它将继承IDisposable,但也实现Dispose(Ex as Exception)重载;这将表现得像IDisposable,除了Ex将作为InnerException传递给IDisposable本身可能抛出的任何异常。事实上,不存在这样的事情。因此,人们可能经常面临吞咽在处置或掩埋在处置之前发生的异常(并且可能首先预测处置)时发生的异常的令人不舒服的选择。两个选择都很糟糕。
  2. 终结者必须避免做任何可能阻止或抛出异常的事情。对于像文件清理这样的东西,有一个文件清理线程(对于一组类,而不是每个对象一个!)等待终结器给它一个信号然后清理文件可能会有所帮助。如果尝试清理文件阻塞,则可能会阻止文件清理线程,但它不会使整个应用程序崩溃。

我并不特别喜欢终结者;重点应放在使处理器工作。