Должны ли 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
.