Можно ли делать возврат изнутри с помощью блока? - PullRequest
27 голосов
/ 06 апреля 2011

Я делаю обзор кода и обнаружил много кода следующего формата:

public MyResponse MyMethod(string arg)
{
    using (Tracer myTracer = new Tracer(Constants.TraceLog))
    {
        MyResponse abc = new MyResponse();

        // Some code

        return abc;
    }
}

Когда я запускаю анализ кода, я получаю предупреждение CA2000 Microsoft.Reliability

Должен ли код быть переписан как:

public MyResponse MyMethod(string arg)
{
   MyResponse abc = new MyResponse();

   using (Tracer myTracer = new Tracer(Constants.TraceLog))
   {
       // Some code
   }
   return abc;
}

Или это не имеет значения?

Редактировать

Строка, в которой он сообщает о предупрежденииis:

MyResponse abc = new MyResponse();

MyResponse - это стандартный набор данных.

Полное сообщение об ошибке:

Предупреждение 150 CA2000: Microsoft.Reliability: In method 'xxxxx(Guid, Guid) ', объект' MyResponse 'расположен не по всем путям исключений.Вызовите System.IDisposable.Dispose для объекта 'MyResponse' до того, как все ссылки на него выйдут из области видимости.

Ответы [ 5 ]

15 голосов
/ 06 апреля 2011

Нет, это не имеет значения.

Блок finally, который неявно генерируется оператором using для обработки удаления, будет выполняться независимо от того, куда вы положили return.

Вы уверены, что CA2000 относится к myTracer, а не abc? Я предполагаю, что предупреждение появляется, потому что MyResponse реализует IDisposable, а вы не утилизируете abc перед возвратом. (В любом случае, предложенное вами переписывание не должно иметь никакого значения для предупреждения.)

11 голосов
/ 06 апреля 2011

Ваше переписывание не исправит это предупреждение CA2000, потому что проблема не в объекте Tracer, а в объекте MyResponse.
В документации говорится:

Ниже приведены некоторые ситуации, когда оператор using недостаточно для защиты объектов IDisposable и может привести к возникновению CA2000.
Для возврата одноразового объекта требуется, чтобы объект был создан в блоке try / finally за пределами блока using.

Чтобы исправить предупреждение , не вмешиваясь в трассировку стека ваших исключений (<- нажмите, это ссылка), используйте этот код: </p>

public MyResponse MyMethod(string arg)
{
   MyResponse tmpResponse = null;
   MyResponse response = null;
   try
   {
       tmpResponse = new MyResponse();

       using (Tracer myTracer = new Tracer(Constants.TraceLog))
       {
           // Some code
       }

       response = tmpResponse;
       tmpResponse = null;
    }
    finally
    {
        if(tmpResponse != null)
            tmpResponse .Dispose();
    }
    return response;
}

Почему?Пожалуйста, смотрите пример в связанной документации.

4 голосов
/ 06 апреля 2011

Предупреждение вероятно о MyResponse, что IDisposable.

Почему появляется предупреждение?

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

Имеет ли это значение?

Вообще говоря, это будет иметь значение, только если:

  • IDisposable инкапсулирует ресурс, который может понадобиться "в ближайшее время" другим частям программы или другому процессу
  • Исключение выдается до возврата метода, чтобы вызвать «проблему»
  • Этот ресурс не высвобождается финализатором достаточно быстро, или , по какой-то причине финализатор никогда не запускается, но ваше приложение не закрывается

Так что нет, это не должно иметь большого значения.

Как это исправить?

public MyResponse MyMethod(string arg)
{
    MyResponse abc = null;
    try {
        abc = new MyResponse();
        using (Tracer myTracer = new Tracer(Constants.TraceLog))
        {
            // Some code
           return abc;
        }
    }
    catch {
        if (abc != null) {
            abc.Dispose();
        }

        throw;
    }
}

Это гарантирует, что если управление выйдет из метода с помощью исключения, abc будет либо null, либо правильно удалено.

Обновление

Оказывается, при использовании этого способа обработки вещей, исключение, явно выброшенное изнутри MyMethod, будет переброшено и будет мутирован номер строки первого кадра стека, чтобы указывать на throw; заявление.

Практически это означает, что если у вас есть несколько throw операторов внутри MyResponse, и они выдают одинаковый тип исключения с одним и тем же сообщением, вы не сможете сказать, какой именно throw был ответственным именно тогда, когда вы ловите исключение.

Это ИМХО чисто академическая проблема, но я упомяну ее для полноты.

2 голосов
/ 06 апреля 2011

Это не имеет значения.Но, в отличие от @Aliostad, я думаю, что версия 2 с return вне блока using - лучший стиль.

Мое обоснование выглядит следующим образом:

usingблок обозначает то, что «открыто» и «закрыто».Это своего рода дешевая сделка.Закрытие блока using говорит о том, что мы выполнили свою работу, и теперь безопасно продолжать заниматься другими делами, такими как return ing.

0 голосов
/ 06 апреля 2011

Это предупреждение, вероятно, связано с принципом «единой точки выхода». Здесь обсуждается: http://c2.com/cgi/wiki?SingleFunctionExitPoint

...