Что происходит в стеке с параметрами ref в c #? - PullRequest
1 голос
/ 28 сентября 2011

Я читаю некоторую документацию C # о WCF и IDispatchMessageInspector, и интерфейс определяет объект «Сообщение», который передается по ссылке, чтобы им можно было манипулировать.

Что на самом деле происходит в стеке, когда вы передаете что-то по ссылке, а не как обычно?

Ответы [ 4 ]

5 голосов
/ 28 сентября 2011

Это не объект , который передается по ссылке - это переменная .

В основном это псевдоним переменной, используемой в качестве аргумента от вызывающей стороны, и параметра в вызываемом методе:

public void Foo()
{
    int x = 10;
    Bar(ref x);
    Console.WriteLine(x); // Prints 20
}

public void Bar(ref int y)
{
    y = 20;
}

Здесь x и y, по сути, являются одной и той же переменной - они ссылаются на одно и то же место хранения. Изменения, сделанные в x, отображаются через y и наоборот. (Обратите внимание, что в этом случае это локальная переменная в вызывающей стороне, но это не обязательно - если вы передали переменную instance по ссылке, тогда Bar может вызвать другой метод, который изменяет та же самая переменная, и тогда y будет "волшебным" изменением ...)

Подробнее о передаче параметров в C # см. Мою статью на тему .

4 голосов
/ 28 сентября 2011

По ссылка означает, что вы можете изменить исходную переменную, переданную элементу. Он в основном передает адрес переменной в стеке, а не значение переменной.

IL Дамп:

Как вы на самом деле спросили , что на самом деле происходит в стеке , здесь дамп IL по ref и по значению метод:

.method private hidebysig instance void  ByRef(string& s) cil managed
{
  // Code size       9 (0x9)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldarg.1
  IL_0002:  ldstr      "New value"
  IL_0007:  stind.ref
  IL_0008:  ret
} // end of method Class1::ByRef

против

.method private hidebysig instance void  ByValue(string s) cil managed
{
  // Code size       9 (0x9)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "New value"
  IL_0006:  starg.s    s
  IL_0008:  ret
} // end of method Class1::ByValue

Как вы можете видеть, основным отличием является тип параметра (string& вместо string) и то, что он делает дополнительные шаги для косвенной загрузки и сохранения значений.

Простой источник C # показан ниже для справки:

void ByRef(ref string s)
{
    s = "New value";
}

void ByValue(string s)
{
    s = "New value";
}
0 голосов
/ 28 сентября 2011

Когда вы передаете что-то по значению; копия типа значения создается и помещается в стек перед вызовом функции. Когда вы передаете что-то по ссылке, адрес этого помещается в стек вместо создания копии, а когда вы изменяете объект в функции, то вместо копии изменяется оригинальный объект.

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

Например, давайте предположим, что в ячейке памяти 100 у вас есть целое число 123; теперь, когда вы вызываете функцию, которая принимает значение по значению (которое является значением по умолчанию), копия 123 будет сделана и помещена в стек перед вызовом функции, что означает, что копия теперь будет иметь (скажем, 160) адрес. Однако, когда вы передаете что-то по ссылке, адрес 100 будет помещен в стек и будет находиться в местоположении 160.

Теперь сгенерированные инструкции будут читать 160, чтобы получить местоположение объекта, а затем изменять данные на 100. Направление будет происходить так же, как это делается для указателей при использовании оператора *.

0 голосов
/ 28 сентября 2011

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

...