Должны ли Dispose() или Finalize() использоваться для удаления временных файлов?

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

Было бы более целесообразно поместить этот код в методы Dispose() или Finalize() вместо этого?

Ответы

Ответ 1

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

Ответ 2

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

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

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

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

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

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

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

Ответ 3

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

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

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

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

Ответ 4

Хороший способ предлагает Дэвид М. Кин в записи 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; 
}

Ответ 5

Я всегда делаю свои классы, указывающие на временные файлы IDisposable, и обычно реализую финализатор, который также вызывает мой метод размещения. Это, по-видимому, парадигма, предложенная 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 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);
}

Ответ 6

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

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

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

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

Ответ 7

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

Ответ 8

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