Как исправить предупреждение компилятора 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
Другие вопросы по тегам