Как исправить предупреждение компилятора C# CA2000 IDisposable при использовании глобального кэша
CA2000 является предупреждением относительно интерфейса IDisposable:
CA2000: Microsoft.Reliability: в методе "ImportProcessor.GetContext(string)" вызовите System.IDisposable.Dispose для объекта "c" до того, как все ссылки на него выйдут из области видимости.
Мой метод используется для хранения кэша контекста следующим образом:
public class RegionContext : IDisposable { /* Implement Dispose() here */ }
private Dictionary<string, RegionContext> contextCache = new ..... ();
public RegionContext GetContext(string regionCode)
{
RegionContext rc = null;
if (!this.contextCache.TryGetValue(regionCode.ToUpper(), out rc))
{
rc = new RegionContext(regionCode);
this.contextCache.Add(regionCode.ToUpper(), rc);
}
return rc;
}
Где бы вы использовали using()
заявление, которое исправляет это предупреждение компилятора?
Мой внешний класс фактически выполняет итерацию и избавляется от содержимого в contextCache
в собственной реализации. Должен ли я подавить это или есть способ правильно избавиться от этого предупреждения?
3 ответа
Это предупреждение CA2000 появляется всякий раз, когда у вас есть возвращаемое значение, которое IDisposable, и не обрабатывает случай, когда метод вызывает исключение. В этом случае вызывающая сторона не получит действительный экземпляр вашего объекта, поэтому у него нет возможности его утилизировать. Поэтому ты должен.
Я предполагаю, что вы не захотите избавиться от объекта, если вы успешно извлечете его из кэша. В этом случае вам нужно будет сделать что-то подобное, чтобы убедиться, что объект, который вы можете создать локально, будет расположен во всех случаях:
public RegionContext GetContext(string regionCode)
{
RegionContext temp = null;
RegionContext rc = null;
try
{
if (!this.contextCache.TryGetValue(regionCode.ToUpper(), out rc))
{
temp = new RegionContext(regionCode);
this.contextCache.Add(regionCode.ToUpper(), temp);
rc = temp;
temp = null;
}
return rc;
}
finally
{
if ( temp != null )
{
temp.Dispose();
}
}
}
CA2000 жалуется здесь на то, что переменная может быть "осиротевшей" в нераспределенном состоянии, если есть исключение при попытке добавить ее в кеш. Чтобы полностью решить проблему, вы можете добавить команду try/catch следующим образом (newContext
переменная используется только для того, чтобы CA2000 мог обнаружить исправление):
public RegionContext GetContext(string regionCode)
{
RegionContext rc = null;
if (!this.contextCache.TryGetValue(regionCode.ToUpper(), out rc))
{
RegionContext newContext = new RegionContext(regionCode);
try
{
this.contextCache.Add(regionCode.ToUpper(), newContext);
}
catch
{
newContext.Dispose();
throw;
}
rc = newContext;
}
return rc;
}
Лично я считаю, что подобные вещи в большинстве случаев несколько нелепы, но...
Решение Михаэля, похоже, не работает при конвертации в VB.Net. Следующие две функции были протестированы в рамках VS 2017:
Public Function OpenStream(ByVal filePathName As String) As System.IO.FileStream
Dim fileStream As System.IO.FileStream = Nothing
Dim tempFileStream As System.IO.FileStream = Nothing
If Not String.IsNullOrWhiteSpace(filePathName) Then
Try
tempFileStream = New System.IO.FileStream(filePathName, System.IO.FileMode.Open, System.IO.FileAccess.Read)
fileStream = tempFileStream
Catch
tempFileStream?.Dispose()
Throw
End Try
End If
Return fileStream
End Function
Public Function OpenReader(ByVal filePathName As String) As System.IO.BinaryReader
If String.IsNullOrWhiteSpace(filePathName) Then Throw New ArgumentNullException(NameOf(filePathName))
If Not System.IO.File.Exists(filePathName) Then Throw New System.IO.FileNotFoundException("Failed opening a binary reader -- file not found.", filePathName)
Dim tempReader As System.IO.BinaryReader = Nothing
Dim reader As System.IO.BinaryReader = Nothing
Dim stream As IO.FileStream = Nothing
Try
stream = Methods.OpenStream(filePathName)
tempReader = New System.IO.BinaryReader(stream)
reader = tempReader
Catch
stream?.Dispose()
tempReader?.Dispose()
Throw
End Try
Return reader
End Function