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

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

Имело бы больше смысла вставлять этот код Расположение () или Завершать () методы вместо этого?

19
задан Eric Anastas 13 July 2010 в 19:59
поделиться

8 ответов

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

.
44
ответ дан 30 November 2019 в 02:05
поделиться

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

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

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

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

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

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

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

12
ответ дан 30 November 2019 в 02:05
поделиться

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

0
ответ дан 30 November 2019 в 02:05
поделиться

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

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

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

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

6
ответ дан 30 November 2019 в 02:05
поделиться

Хороший способ предлагает Дэвид М. Кин в записи 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; 
}
3
ответ дан 30 November 2019 в 02:05
поделиться

Если вы хотите повторно использовать свои временные файлы, например 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
ответ дан 30 November 2019 в 02:05
поделиться

Я всегда делаю свои классы, которые указывают на временные файлы 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);
}
1
ответ дан 30 November 2019 в 02:05
поделиться

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

0
ответ дан 30 November 2019 в 02:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: