Когда значение параметра out или out в C # действительно возвращается вызывающей стороне? - PullRequest
14 голосов
/ 08 января 2010

Когда я делаю назначение параметру 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?
}

Ответы [ 2 ]

26 голосов
/ 08 января 2010

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

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

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

14 голосов
/ 08 января 2010

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

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

...