Long story short: у нас в CodeJam среди кучи других полезняшек есть микро-хелпер для работы с временными файлами: завёл-поработал-оноавтоматомгрохнулось.
Выглядит как-то так:
using (var tempFile = TempData.CreateFile())
{
DoSomethingWith(tempFile.Path);
}
// file is deleted for now
И всё вроде было отлично, пока
не пришёл уважаемый Vasiliy2Автор: Vasiliy2
Дата: 22.09.16
и не задал нехороший вопрос:
а что нужно делать, если временный файл удалить не получается? С контрольным аргументом: куча последовательных вызовов выкушает всё место и наступит полная печаль
Текущее поведение вроде как соответствует FDG (ага, нет явных доводов за любую позицию — обращайся к FDG) и проглатывает исключения, связанные с ошибками удаления файла. Молча проглатывает. Почему так — см
вот этуАвтор: Sinix
Дата: 22.09.16
кучу цитат. Если коротко — сигналить "всё пропало, шеф" при диспозе не очень хорошо, т.к. это может привести к ещё худшим последствиям.
И теперь собственно вопрос: а как быть-то? Основные варианты собраны
вот тут, кому лень открывать —
| скопировал |
| public class TempDataUseCases
{
public void CaseA_NoHelpers()
{
// weird one.
var fileName = "1.txt";
Exception deletedEx = null;
try
{
File.Create(fileName).Close();
Process(fileName);
}
finally
{
try
{
File.Delete(fileName); // we do expect this will throw.
}
catch (Exception ex)
{
deletedEx = ex;
}
}
if (deletedEx != null)
{
HandleDeleteFailure(fileName, deletedEx);
}
}
public void CaseB_LetItCrash()
{
// ok if failure is handled by caller
using (var tempFile = TempData.CreateFile(throwOnDisposeFailure: true))
{
Process(tempFile.FileName);
} // dispose will throw
}
public void CaseC_LetItCrashHandle()
{
// BAD BAD BAD: we need to store fileName somewhere.
string fileName = null;
try
{
using (var tempFile = TempData.CreateFile(throwOnDisposeFailure: true))
{
fileName = tempFile.FileName;
Process(tempFile.FileName);
}
}
catch (Exception ex) when (fileName != null) // HACK. Will not work on C#5 or below.
{
HandleDeleteFailure(fileName, ex);
}
}
public void CaseD_TryDelete()
{
using (var tempFile = TempData.CreateFile())
{
Process(tempFile.FileName);
if (!tempFile.TryClose())
{
HandleDeleteFailure(tempFile.FileName, null); // no exception info available.
}
}
}
public void CaseE_EnsureDeleted()
{
// kinda ok
using (var tempFile = TempData.CreateFile())
{
Process(tempFile.FileName);
try
{
tempFile.EnsureDelete();
}
catch (Exception ex)
{
HandleDeleteFailure(tempFile.FileName, ex);
}
}
}
public void CaseF_ManualDelete()
{
// kinda ok
using (var tempFile = TempData.CreateFile())
{
Process(tempFile.FileName);
try
{
File.Delete(tempFile.FileName);
}
catch (Exception ex)
{
HandleDeleteFailure(tempFile.FileName, ex);
}
}
}
public void CaseG_Fallback()
{
// The best?
using (var tempFile = TempData.CreateFile(
(f, ex) => HandleDeleteFailure(f.FileName, ex)))
{
Process(tempFile.FileName);
}
}
private void Process(string tempFile) { }
private void HandleDeleteFailure(string fileName, Exception exception) { }
}
// Stub implementation. Unusable by design.
public class TempData : IDisposable
{
public static TempData CreateFile() => new TempData();
public static TempData CreateFile(bool throwOnDisposeFailure) => new TempData();
public static TempData CreateFile(Action<TempData, Exception> deleteFallback) => new TempData();
public string FileName => null;
public void EnsureDelete()
{
throw new IOException("Cannot delete the file.");
}
public bool TryClose() => false;
public void Dispose()
{
// ok or throw
}
}
|
| |
Варианты и предложения приветствуются
UPD. И да, FileOptions.DeleteOnClose подходит не всегда. Чтоб не спорить — у нас точно такой же хелпер есть для директорий. Что с ними делать будем? ; )