Когда значение параметра С# 'out' или 'ref' действительно возвращается вызывающему?

Когда я выполняю назначение параметру out или ref, это значение, которое сразу назначается ссылкой, предоставленной вызывающим, или значения параметра out и ref, назначенные ссылкам, когда метод возвращается? Если метод генерирует исключение, возвращаемые значения?

Например:

int callerOutValue = 1;
int callerRefValue = 1;
MyMethod(123456, out callerOutValue, ref callerRefValue);

bool MyMethod(int inValue, out int outValue, ref int refValue)
{
    outValue = 2;
    refValue = 2;

    throw new ArgumentException();

    // Is callerOutValue 1 or 2?
    // Is callerRefValue 1 or 2?
}

Ответы

Ответ 1

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

Это означает, что в вашем примере выше (если вы, конечно, поймаете ArgumentException), outValue и refValue будут установлены равными 2.

Также важно отметить, что out и ref являются идентичными концепциями на уровне IL - только компилятор С# обеспечивает дополнительное правило для out, которое требует, чтобы метод устанавливал его значение до возвращение. Таким образом, с точки зрения CLR outValue и refValue имеют идентичную семантику и обрабатываются одинаково.

Ответ 2

Эндрю прав; Я просто добавлю несколько дополнительных деталей.

Во-первых, правильный способ думать о параметрах out/ref состоит в том, что они являются псевдонимами для переменных. То есть, когда у вас есть метод M (ref int q) и называть его M (ref x), q и x - два разных имени для одной и той же переменной. Переменная - это место хранения; вы храните что-то в q, вы также храните его в x, потому что это два разных имени для одного и того же местоположения.

Во-вторых, альтернатива, которую вы описываете, называется ссылкой "копировать/копировать". В этой схеме есть два места хранения, а содержимое одного копируется при начале вызова функции и копируется обратно, когда это делается. Как вы заметили, семантика copy-in-copy-out отличается от семантики ссылок псевдонимов при выдаче исключений.

Они также отличаются в таких странных ситуациях, как это:

void M(ref int q, ref int r)
{  
  q = 10;
  r = 20;
  print (q);
}

...

M(ref x, ref x);

В aliasing, x, q и r - все одно и то же место хранения, поэтому это печатает 20. В ссылке на копирование в копии это будет печатать 10, а окончательное значение x будет зависеть от того, будет ли копия - выходили слева направо или налево.

Наконец, если я правильно помню, в реализации деревьев выражений встречаются редкие и причудливые сценарии, в которых мы фактически реализуем семантику копирования-в-копировании по параметрам ref. Я должен просмотреть этот код и посмотреть, могу ли я вспомнить, что именно эти сценарии.