Подтвердить что ты не робот

Исходные параметры и исключения

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

    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.
Гарантируется ли это спецификацией или зависит от реализации? (Я искал спецификацию С# 3, но не смог узнать сам)

4b9b3361

Ответ 1

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

Ответ 2

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

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

из MSDN

Ответ 3

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

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

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

string s;
try {
  Fjuk(out s);
  Console.WriteLine(s); // here the variable is guaranteed to be set
} catch (Exception) {
  Console.WriteLine(s); // here it not, so this won't compile
}

Ответ 4

Это гарантировано с точки зрения 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 > должны быть запрещены.