Сделать 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
приводит к его коробке?
Как выглядит фактический стек во время рекурсивного вызова?
Ответы
Ответ 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). Они перемещаются. Они безопасны и поддаются проверке.