Следует ли использовать Dispose () или Finalize () для удаления временных файлов? - PullRequest
16 голосов
/ 13 июля 2010

У меня есть класс, который использует временные файлы (Path.GetTempFileName()), пока он активен. Я хочу убедиться, что эти файлы не остаются на жестком диске пользователя, занимая место после закрытия моей программы. Прямо сейчас в моем классе есть метод Close(), который проверяет, существуют ли еще временные файлы, используемые классом, и удаляет их.

Имеет ли смысл вместо этого помещать этот код в методы Dispose () или Finalize ()?

Ответы [ 8 ]

43 голосов
/ 14 июля 2010

Еще лучше было бы создать файл с FileOptions.DeleteOnClose.Это гарантирует, что операционная система принудительно удалит файл при выходе из вашего процесса (даже в случае грубого прерывания).Конечно, вы все равно захотите закрыть / удалить файл самостоятельно, когда закончите с ним, но это обеспечивает хорошую защиту, чтобы вы не позволяли файлам сидеть без дела вечно

10 голосов
/ 14 июля 2010

Я бы сделал оба; сделайте класс одноразовым, и пусть финализатор очистит его. Существует стандартная схема, позволяющая сделать это безопасно и эффективно: используйте ее вместо того, чтобы пытаться определить для себя, каков правильный шаблон. Очень легко ошибиться. Прочитайте это внимательно :

http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

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

  • Существуют всевозможные потенциалы для условий гонки или взаимоблокировок, потому что вы больше не в главном потоке, вы в потоке финализатора.

  • В обычном коде, если вы запускаете код внутри объекта, вы знаете, что все объекты, на которые ссылается объект, живы. В финализаторе все объекты, на которые ссылается объект, могли быть только что завершены! Финализаторы мертвых объектов могут запускаться в любом порядке, включая «дочерние» объекты, финализируемые перед «родительскими» объектами.

  • В обычном коде назначение ссылки на объект в статическое поле может быть вполне разумным. В финализаторе ссылка, которую вы назначаете, может быть на уже мертвый объект , и поэтому назначение возвращает мертвый объект к жизни. (Поскольку объекты, на которые ссылаются статические поля, всегда живы.) Это странное очень странное состояние, и ничего приятного не произойдет, если вы это сделаете.

  • И так далее. Будьте осторожны. Ожидается, что полностью поймет, как работает сборщик мусора, если вы напишите нетривиальный финализатор.

6 голосов
/ 14 июля 2010

Файл является неуправляемым ресурсом, и вы реализуете IDisposable для очистки неуправляемых ресурсов, от которых зависят ваши классы.

Я реализовал подобные классы, хотя никогда не применялся в рабочем коде.

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

Я бы сказал, что реализация IDisposable будетразумный выбор.

3 голосов
/ 14 июля 2010

Дэвид М. Кин (David M. Kean) предлагает хороший способ записи MSDN на Path.GetTempFileName. Он создает класс-оболочку, реализующий IDisposable, который автоматически удалит файл:

public class TemporaryFile : IDisposable
{
    private bool _isDisposed;

    public bool Keep { get; set; }
    public string Path { get; private set; }

    public TemporaryFile() : this(false)
    {
    }

    public TemporaryFile(bool shortLived)
    {
        this.Path = CreateTemporaryFile(shortLived);
    }

    ~TemporaryFile()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (!_isDisposed)
        {
            _isDisposed = true;

            if (!this.Keep)
            {
                TryDelete();   
            }
        }
    }

    private void TryDelete()
    {
        try
        {
            File.Delete(this.Path);
        }
        catch (IOException)
        {
        }
        catch (UnauthorizedAccessException)
        {
        }
    }

    public static string CreateTemporaryFile(bool shortLived)
    {
        string temporaryFile = System.IO.Path.GetTempFileName();

        if (shortLived)
        { 
            // Set the temporary attribute, meaning the file will live 
            // in memory and will not be written to disk 
            //
            File.SetAttributes(temporaryFile, 
                File.GetAttributes(temporaryFile) | FileAttributes.Temporary);
        }

        return temporaryFile;
    }
}

Использовать новый класс легко, просто наберите следующее:

using (TemporaryFile temporaryFile = new TemporaryFile())
{
    // Use temporary file
}

Если после создания TemporaryFile вы решили, что хотите предотвратить его удаление, просто установите для свойства TemporaryFile.Keep значение true:

using (TemporaryFile temporaryFile = new TemporaryFile()) 
{ 
    temporaryFile.Keep = true; 
}
1 голос
/ 14 июля 2010

Если вы хотите повторно использовать ваши временные файлы, например open \ close \ read \ write \ etc, то очистка их на уровне выгрузки AppDomain может быть полезной.

Это можно использовать в сочетании спомещая временные файлы в хорошо известный подкаталог временного местоположения и следя за тем, чтобы этот каталог удалялся при запуске приложения, чтобы обеспечить выполнение грязных отключений.

Базовый пример метода (собработка исключений удалена вокруг удаления для краткости).Я использую эту технику в модульных тестах на основе файлов, где она имеет смысл и полезна.

public static class TempFileManager
{
    private static readonly List<FileInfo> TempFiles = new List<FileInfo>();
    private static readonly object SyncObj = new object();

    static TempFileManager()
    {
        AppDomain.CurrentDomain.DomainUnload += CurrentDomainDomainUnload;
    }

    private static void CurrentDomainDomainUnload(object sender, EventArgs e)
    {
        TempFiles.FindAll(file => File.Exists(file.FullName)).ForEach(file => file.Delete());
    }

    public static FileInfo CreateTempFile(bool autoDelete)
    {
        FileInfo tempFile = new FileInfo(Path.GetTempFileName());

        if (autoDelete)
        {
            lock (SyncObj)
            {
                TempFiles.Add(tempFile);
            }
        }

        return tempFile;
    }
}
1 голос
/ 14 июля 2010

Я всегда создаю свои классы, которые указывают на временные файлы IDisposable, и обычно реализую финализатор, который также вызывает мой метод dispose.Похоже, что это парадигма, предложенная IDisposable MSDN-страницей .

Связанный код ниже:

public void Dispose()
{
    Dispose(true);
    // This object will be cleaned up by the Dispose method.
    // Therefore, you should call GC.SupressFinalize to
    // take this object off the finalization queue
    // and prevent finalization code for this object
    // from executing a second time.
    GC.SuppressFinalize(this);
}

// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
private void Dispose(bool disposing)
{
    // Check to see if Dispose has already been called.
    if(!this.disposed)
    {
        // If disposing equals true, dispose all managed
        // and unmanaged resources.
        if(disposing)
        {
            // Dispose managed resources.

        }

        // Call the appropriate methods to clean up
        // unmanaged resources here.
        // If disposing is false,
        // only the following code is executed.


        // Note disposing has been done.
        disposed = true;

    }
}



// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
~MyResource()
{
    // Do not re-create Dispose clean-up code here.
    // Calling Dispose(false) is optimal in terms of
    // readability and maintainability.
    Dispose(false);
}
0 голосов
/ 14 июля 2010

Вы обязательно должны использовать Dispose для очистки ресурсов, но убедитесь, что вы реализуете интерфейс IDisposable. Вы не хотите просто добавить метод с именем Dispose.

0 голосов
/ 14 июля 2010

Абсолютно.Таким образом, вы можете обеспечить очистку при наличии исключений.

...