2010-01-07 9 views
14

Когда я выполняю назначение параметра out или ref, является ли значение, которое сразу назначено ссылочной позиции, предоставленной вызывающим, или значения out и ref, назначенные ссылочным ссылкам при возврате метода? Если метод генерирует исключение, возвращаемые значения?Когда значение параметра C# '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? 
} 

ответ

26

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

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

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

+1

Хм ... это не то, что я ожидал бы, но тест подтверждает это! +1. –

+0

Это полезная информация. : O – Sapph

+0

Я помню микрооптимизацию метода, который получил параметр 'out'. Метод был вызван в очень сложном цикле, и я хотел удалить требуемую инициализацию каждый раз, когда он был вызван, поскольку параметр не изменился, поэтому я заставил его принять параметр 'ref', и он выполнялся значительно хуже. У кого-нибудь есть объяснение, или это была случайность? – JulianR

14

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

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

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

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

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

... 

M(ref x, ref x); 

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

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