Почему мой наконец блок не работает в C #? - PullRequest
6 голосов
/ 24 марта 2009

Я помогал коллеге отлаживать странное поведение в их коде. Следующий пример иллюстрирует это:

static void Main(string[] args)
{
    string answer = Sample();
    Console.WriteLine(answer);
}

public static string Sample()
{
    string returnValue = "abc";

    try 
    {
         return returnValue;
    }

    catch (Exception)
    {
         throw;
    }

    finally
    {
         returnValue = "def";
    }
}

Что возвращает этот образец?

Вы думаете, что из-за блока finally он возвращает «def», но на самом деле он возвращает «abc»? Я прошел по коду и подтвердил, что на самом деле вызывается блок finally.

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

Редактировать: для уточнения потока на основе некоторых ответов.

Когда вы шагаете по коду, finally выполняется перед возвратом.

Дубликат: Что на самом деле происходит при попытке {return x; } наконец {х = ноль; } заявление?

Ответы [ 7 ]

12 голосов
/ 24 марта 2009

Ваш блок "finally" присваивает значение returnValue, а не возвращает значение. «Возврат» уже произошел до того, как блок finally меняет значение, и поэтому возвращается «abc».

Хотя код сбивает с толку, поскольку то, что вы сделали, не имеет смысла, то, что он делает, правильно.

3 голосов
/ 24 марта 2009

Да, блок finally запускается после возврата из функции, но это не имеет значения. Помните, что возвращаемое значение передается по значению, поэтому для него создается временная переменная во время возврата, поэтому блок finally не влияет на фактическое возвращаемое значение. Если вы хотите поддерживать желаемое поведение, вы можете использовать параметр out, например:

static void Main(string[] args)
{
    string answer;
    Sample(out answer);
    Console.WriteLine(answer);
}

public static void Sample(out string answer)
{

    try
    {
        answer = "abc";
        return;
    }

    catch (Exception)
    {
        throw;
    }

    finally
    {
        answer = "def";
    }
}

Или вы можете просто переместить оператор return за пределы блока try, например:

static void Main(string[] args)
{
    string answer = Sample();
    Console.WriteLine(answer);
}

public static string Sample()
{
    string returnValue;
    try
    {
        returnValue = "abc";
    }

    catch (Exception)
    {
        throw;
    }

    finally
    {
        returnValue = "def";
    }

    return returnValue;
}

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

3 голосов
/ 24 марта 2009

Блок finally эффективно запускает после оператора return. Итак, вы уже вернули старое значение abc, прежде чем перейти в свой блок finally.

(это не совсем то, как работает под капотом, но это достаточно близко для смысла здесь)

2 голосов
/ 24 марта 2009

Найдена эта ссылка некоторое время назад, которая занимается именно этим вопросом. Он старается показать код IL, который точно показывает, что происходит.

0 голосов
/ 24 марта 2009

В предположении я бы сказал, что вы определяете, что будет возвращено (ссылка на строку «abc») в точке, где находится оператор return.

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

0 голосов
/ 24 марта 2009

Если вам действительно интересно, что происходит, тогда вы можете скачать и установить Reflector . Это фантастический инструмент, который можно использовать в своих «хитростях». Он расскажет вам, что происходит под капотом.

0 голосов
/ 24 марта 2009

Я не эксперт, но я должен был бы догадаться, что эта функция вернется, и затем наконец вызовется. Поскольку return returnValue уже выполнено, не имеет значения, какое значение returnValue принимает в блоке finally. Такое поведение имеет смысл, поскольку - это , предназначенный для выполнения всего блока try перед блоком finally, и единственный способ, которым он может это сделать, - это если он возвращается из функции, как предполагалось.

...