C # Использование IDisposable для очистки временных файлов - PullRequest
9 голосов
/ 05 ноября 2010

У меня есть класс FileUploader, которому по желанию может быть предоставлен zip-файл, который он извлекает во временную папку и возвращает пути к файлам.

Из того, что я понял, реализация интерфейса IDisposable в классе FileUploader и использование метода Dispose для удаления всех временных файлов приведут в порядок сам класс, как только его ссылка выйдет из контекста?

Хотя, похоже, это не тот случай - кто-нибудь может объяснить, как я могу поступить с тем, чего я пытаюсь достичь?

UPDATE
Чтобы уточнить, мой код:

    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();
    }

Я пытаюсь заставить FileUploader удалить все временные файлы после завершения метода ImportFile ()

Ответы [ 7 ]

7 голосов
/ 05 ноября 2010

Вам нужно будет правильно реализовать 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
}

Затем используйте класс внутри блока using в вашем методе main следующим образом:

using(TempStorage myStorage = new TempStorage("C:\temp")
{
    // rest of the main method here...
}
7 голосов
/ 05 ноября 2010

«выпал из контекста» неясно; Вы должны были бы сделать:

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();
}

и именно это вызывает детерминированную очистку.

2 голосов
/ 05 ноября 2010

Dispose автоматически, только если вы переносите контекст с ключевым словом using:

using (FileUploader uploader = new FileUploader(...))
    uploader.UploadFiles();
1 голос
/ 05 ноября 2010

Вы можете реализовать финализаторы так, что если вы забудете вызвать Dispose (что вам действительно не следует!) И вызвать Dispose в вашем финализаторе.Это будет вызвано, когда объект будет собирать мусор, но он не является детерминированным, т.е. нет способа узнать, когда он будет вызван.

Но это не гарантируется .

0 голосов
/ 21 ноября 2010

IDisposable подходит в тех случаях, когда объект изменяет что-то вне себя таким образом, что в какой-то момент его необходимо очистить, прежде чем объект исчезнет. Казалось бы, создание временных файлов оправдано. Есть, однако, пара предостережений:

  1. К сожалению, у диспетчера нет хорошего способа определить, ожидает ли исключение время его выполнения. Если бы у меня были мои детекторы, был бы интерфейс IDisposeableEx, который наследовал бы IDisposable, но также реализовывал бы перегрузку Dispose (Ex as Exception); это будет вести себя как IDisposable, за исключением того, что Ex будет передан как InnerException для любого исключения, которое может выдать сам IDisposable. Как таковой, такой вещи не существует. Таким образом, часто можно столкнуться с неудобным выбором глотания исключения, которое происходит при утилизации, или захоронения исключения, которое произошло до утилизации (и, возможно, предопределило удаление в первую очередь). Оба варианта воняют.
  2. Финализаторы должны избегать действий, которые могут блокировать или выдавать исключение. Для чего-то вроде очистки файла может быть полезно иметь поток очистки файла (для группы классов, а не по одному на объект!), Который ожидает, пока финализатор подаст ему сигнал, а затем очистит файлы. Если попытка очистить файл блокирует, поток очистки файла может быть заблокирован, но это не приведет к сглаживанию всего приложения.

Мне не особо нравятся финализаторы; следует сосредоточиться на том, чтобы заставить работать утилизатор.

0 голосов
/ 05 ноября 2010

Как и все остальные, лучше всего заключить его в оператор использования, чтобы вызвать метод Dispose.Я считаю, что именно так Microsoft рекомендует использовать все, что реализует IDispose, по крайней мере, на основе своих правил анализа кода.

0 голосов
/ 05 ноября 2010

Как уже говорили другие, Dispose не вызывается автоматически, если контекст не обернут ключевым словом using.Однако вы могли бы реализовать шаблоны Dispose и Finalize (вызвать метод Dispose из финализатора / деструктора).Таким образом, у вас есть отказоустойчивая система, которая запускает и удаляет ваши временные файлы, даже если ваш код не вызывает Dispose напрямую (поскольку сборщик мусора в конечном итоге вызовет финализатор).

Этостатья иллюстрирует концепцию, а также дает некоторое представление о том, как работает финализация.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...