Подтвердить что ты не робот

Сделать int ref параметр в коробке?

Скажем, у меня есть следующий код:

void Main()
{
    int a = 5;
    f1(ref a);
}

public void f1(ref int a)
{
    if(a > 7) return;
    a++;
    f1(ref a);
    Console.WriteLine(a);
}

Выход:

8 8 8 

i.e., когда пакет освобождает значение параметра ref.

Означает ли это, что добавление ref keyword в int parameter приводит к его коробке?
Как выглядит фактический стек во время рекурсивного вызова?

4b9b3361

Ответ 1

Передача значения по ссылке приводит к тому, что его позиция в стеке передается, а не сама величина. Это не имеет никакого отношения к боксу и распаковке. Это заставляет думать о том, как стек выглядит во время рекурсивных вызовов довольно легко, так как каждый отдельный вызов относится к "тому же" местоположению в стеке.

Я думаю, что большая путаница возникает из пункта MSDN по боксу и распаковке:

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

Возможно, вы сбиваете с толку вас между двумя разными способами: 1) "преобразование", как вам будет, типа значения, чтобы сказать объект, который по определению является ссылочным типом:

int a = 5;
object b = a; // boxed into a reference type

и 2) с передачей параметра типа значения по ссылке:

main(){
   int a = 5;
   doWork(ref a);
}
void doWork(ref int a)
{
    a++;
}

Какие две разные вещи.

Ответ 2

Легко создать программу, которая могла бы дать разные результаты в зависимости от того, будет ли ref int вставлен в коробку:

static void Main()
{
    int a = 5;
    f(ref a, ref a);
}

static void f(ref int a, ref int b)
{
    a = 3;
    Console.WriteLine(b);
}

Что вы получаете? Я вижу напечатанную 3.

Бокс включает создание копий, поэтому, если ref a был помещен в бокс, выход был бы 5. Вместо этого оба a и b являются ссылками на исходную переменную a в Main. Если это помогает, вы можете в основном (не совсем) думать о них как о указателях.

Ответ 3

Ваш Console.WriteLine(a); будет выполнен после завершения рекурсии. Рекурсия завершается, когда значение int равно 8. И чтобы сделать это до 8, она имеет рекурсию в 3 раза. Таким образом, после этого он будет печатать 8, а затем передать управление рекурсии, выше которой снова будет печатать 8, поскольку значение переменной указано 8.

Также проверьте вывод ILDASM

.method public hidebysig static void  f1(int32& a) cil managed
{
  // Code size       26 (0x1a)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldind.i4
  IL_0002:  ldc.i4.7
  IL_0003:  ble.s      IL_0006
  IL_0005:  ret
  IL_0006:  ldarg.0
  **IL_0007:  dup**
  IL_0008:  ldind.i4
  IL_0009:  ldc.i4.1
  IL_000a:  add
  IL_000b:  stind.i4
  IL_000c:  ldarg.0
  IL_000d:  call       void ConsoleApplication1.Program::f1(int32&)
  IL_0012:  ldarg.0
  IL_0013:  ldind.i4
  IL_0014:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_0019:  ret
} // end of method Program::f1

Ответ 4

Я думаю, вы ошибаетесь, говоря, что параметр int помещается в коробку. Из MSDN,

Бокс - это процесс преобразования типа значения в объект типа или к любому типу интерфейса, реализованному этим типом значения

То, что у вас здесь есть параметр int, передаваемый по ссылке, в частности, это "тип значения", передаваемый по ссылке.

Вы можете сослаться на Jon Skeet отлично пояснение о передаче параметров для деталей.

Ответ 5

Это не бокс.

В MSDN есть ясное объяснение ref keyword документация:

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

Ответ 6

Добавление к существующим ответам, как это реализовано:

CLR поддерживает так называемые управляемые указатели. ref передает управляемый указатель на переменную в стеке. Вы также можете передать места кучи:

var array = new int[1];
F(ref array[0]);

Вы также можете передавать ссылки на поля.

Это не приводит к закреплению. Управляемые указатели понимаются во время выполнения (в частности, GC). Они перемещаются. Они безопасны и поддаются проверке.