Выходные параметры и исключения - PullRequest
31 голосов
/ 18 января 2012

Скажите, что у меня есть следующий код:

    static void Fjuk(out string str)
    {
        str = "fjuk!";
        throw new Exception();
    }

    static void Main(string[] args)
    {
        string s = null;
        try
        {
            Fjuk(out s);
        }
        catch (Exception)
        {
            Console.WriteLine(s ?? "");
        }
    }

Когда я проверял его, s был инициализирован как "fjuk!"когда он используется в блоке catch.
Это гарантировано спецификацией или зависит от реализации?(Я искал спецификацию C # 3, но сам не смог найти)

Ответы [ 5 ]

26 голосов
/ 18 января 2012

В значительной степени это аспект того, что означает out; во-первых, обратите внимание, что out на самом деле не существует - нам действительно нужно учитывать ref (out - это просто ref с некоторыми настройками "определенного назначения" в компиляторе). ref означает «передать адрес этого» - если мы изменим значение через адрес, то это покажет немедленно - это, в конце концов, обновление памяти в стеке Main. Он не может абстрагировать это (чтобы задержать запись), потому что значением может быть, например, некоторая негабаритная структура, использующая ref специально для того, чтобы избежать копирования в стек (подход, широко используемый в XNA и т. Д.) .

7 голосов
/ 18 января 2012

Это "гарантировано" , поскольку параметр out изменяет значение с помощью параметра memory address.

Ключевое слово out вызывает передачу аргументов по ссылке.Это похоже на ключевое слово ref, за исключением того, что ref требует инициализации переменной перед ее передачей.

из MSDN

4 голосов
/ 18 января 2012

Если метод выдает исключение, выходной параметр не гарантируется. Если метод завершается без исключения, выходной параметр гарантированно будет установлен.

В вашем случае метод всегда устанавливает выходной параметр, но компилятор не анализирует код метода таким образом. Если метод завершается с исключением, выходной параметр все еще не считается установленным.

Ваш код в обработчике исключений не зависит от переменной, устанавливаемой вызовом метода, поскольку вы устанавливаете переменную при ее создании. Если вы не установите переменную при ее создании, обработчик исключений не сможет ее использовать, поскольку она не гарантированно будет установлена:

string s;
try {
  Fjuk(out s);
  Console.WriteLine(s); // here the variable is guaranteed to be set
} catch (Exception) {
  Console.WriteLine(s); // here it's not, so this won't compile
}
2 голосов
/ 18 января 2012

Это гарантировано с точки зрения Fjuk, но не Main.

В Fjuk возникает исключение после установки параметра.Хотя компилятором, джиттером и процессором могут быть переупорядочения, переупорядочения не будут такими, что порядок, наблюдаемый одним потоком, изменится.Поскольку один поток мог «заметить», что параметр не был установлен перед созданием исключения, параметр гарантированно будет установлен.

В Main, однако мы не знаем деталей Fjuk реализация, поэтому, когда компилятор анализирует Main, это не может зависеть от этого.Следовательно, в варианте, в котором мы не присваиваем значение s до вызова:

static void Main()
{
    string s;
    try
    {
        Fjuk(out s);
        Console.WriteLine(s ?? "");//fine
    }
    catch (Exception)
    {
        Console.WriteLine(s ?? "");//compiler error
    }
    Console.WriteLine(s ?? "");//compiler error
}

Первая попытка использовать s сразу после вызова Fjuk - это нормально, потому чтоможет попасть туда только в случае успеха Fjuk, а в случае успеха Fjuk необходимо присвоить s.Во втором и третьем случаях, тем не менее, можно прийти к этим строкам без успешного выполнения Fjuk, и, поскольку анализом Main невозможно определить, можно ли выдать исключение до установки s, использование s должно быть запрещено.

0 голосов
/ 13 марта 2018

Из раздела C # Language Specification 5.1.6 Выходные параметры

После нормального завершения вызова члена функции или делегата каждая переменная, переданная в качестве выходного параметрасчитается назначенным в этом пути выполнения.

Другими словами

  • out параметры всегда назначаются.
  • любое предыдущее значение перезаписывается.
  • но если функция выдает исключение, компилятор предполагает, что они не назначены.

На практике

  • Вам не нужно беспокоиться об этом.
  • Если вы попытаетесь использовать неназначенную переменную, компилятор выдаст ошибку.
  • Так что выходные параметры в C # полностью безопасны - если они компилируются, они работают.
...