Когда значение параметра С# '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. Я должен просмотреть этот код и посмотреть, могу ли я вспомнить, что именно эти сценарии.