No, это неправильный номер.
ref
Приводит к таблице эталонная семантика.
При вызове функции foo(T arg)
- независимо от того, T является типом значения или ссылочный тип (я не могу не подчеркнуть, что достаточно) - foo
получает копию T.
Разницы между типами значений и ссылочными типами является то, что имен типов значений относятся к интересному материалу, в то время как имен ссылочных типов относятся к ссылкам (или указателям) на интересные вещи. Из этого следует, что когда вы копируете объект типа значения, вы получаете копию интересного материала и когда вы копируете упомянутую ссылку на интересный материал.
Некоторых изображений я нашел на Google вместо рисования один на моем собственном:
Копия в изображении, в основном то, что происходит, когда вы называете foo(T arg
) это говорит. В обоих случаях мы arg
содержит копию того, что было передано foo
. Если T
является int
этого числа вы заинтересованы. Если T
является Circle
то arg
является «адресом» , что аргумент, передаваемый foo
содержал. Вместо того, чтобы говорить о указателях или ссылках, мы можем говорить о стрелках. переменные ссылочных типов содержат стрелки для интересного материала, в то время как переменные типов значений содержат сам материал.
Это демонстрируется следующим кодом:
using System;
class MainClass {
public static void Main (string[] args) {
Foo foo1 = new Foo();
foo1.i = 1;
Console.WriteLine("Main(): foo1.i: {0}", foo1.i);
doStuffToFoo(foo1, 5);
Console.WriteLine("Main(): foo1.i: {0}", foo1.i);
Console.WriteLine();
Bar bar1;
bar1.j = 1;
Console.WriteLine("Main(): bar1.j: {0}", bar1.j);
doStuffToBar(bar1, 5);
Console.WriteLine("Main(): bar1.j: {0}", bar1.j);
}
static void doStuffToFoo(Foo aFoo, int aInt) {
aFoo.i = aInt;
aFoo = new Foo();
aFoo.i = 123;
Console.WriteLine("doStuffToFoo: aFoo.i: {0}", aFoo.i);
}
static void doStuffToBar(Bar aBar, int aInt) {
aBar.j = aInt;
aBar = new Bar();
aBar.j = 123;
Console.WriteLine("doStuffToBar: aBar.j: {0}", aBar.j);
}
}
class Foo {
public int i;
}
struct Bar {
public int j;
}
Смотрите, если вы понимаете, что он печатает, прежде чем пытаться или читать дальше.
Переменная aFoo
внутри doStuffToFoo
является копия из foo1
. Легко и просто. aFoo
и foo1
являются различными переменными (хранящимся в различных местах на стеке - foo1
в Main
«кадра стека с и aFoo
в doStuffToFoo
» кадра стека с - при условии что стопка, так как the stackis an implementationdetail), и они оба содержат стрелку к такой же Foo
объект.
Когда вы ссылаетесь на поля обеих этих переменных, они получают доступ к тому же объекту Foo
через соответствующие стрелки. Но когда вы назначаете new Foo()
на aFoo
, он меняет только стрелку в aFoo
, чтобы указать на новый объект Foo
. Он ничего не делает с другой стрелкой к первому объекту Foo
, который безопасно хранится внутри переменной foo1
и недоступен для кода в doStuffToFoo
. Вот почему вы не видите «123» назад в Main
.
И все это приводит нас к ref
. ref T v
говорит, что v
не является новой переменной типа T (которая содержит некоторые интересные вещи, если T
- тип значения или стрелка для некоторых интересных вещей, если T
является ссылочным типом). Скорее v
является псевдоним тому, что передается как аргумент. Это второе имя для некоторой другой переменной.
Если вы добавите следующий код в приведенный выше пример, вы увидите, что это действительно так.
/***** inside Main: *****/
Foo foo2 = new Foo();
Foo foo3 = foo2;
foo2.i = 1;
Console.WriteLine("Main(): foo2.i: {0}", foo2.i);
Console.WriteLine("Main(): foo3.i: {0}", foo3.i);
doStuffToFooRef(ref foo2, 5);
Console.WriteLine("Main(): foo2.i: {0}", foo2.i);
Console.WriteLine("Main(): foo3.i: {0}", foo3.i);
/***** until here *****/
static void doStuffToFooRef(ref Foo aFoo, int aInt) {
aFoo.i = aInt;
aFoo = new Foo();
aFoo.i = 123;
Console.WriteLine("doStuffToFooRef: aFoo.i: {0}", aFoo.i);
}
Теперь aFoo
это другое название для foo2
. Когда вы назначаете «aFoo
», вы назначаете «foo2
», поскольку оба эти имени относятся к одной и той же переменной (место хранения). Это то, что происходит во время выполнения задания в doStuffToFooRef
:
(i
по-прежнему равна нулю, так как это, прежде чем положить туда 123)
Попробуйте здесь: https://repl.it/E80a
C# ref
, как C++ ссылаясь на это, как писал Александр Лысенко в своем ответе. В то время как технически ref SomeReferenceType x
может быть «указатель на указатель», как утверждают некоторые, это не улавливает реальный смысл ref
и останавливается на деталях реализации, которые не обязательно всегда верны (это может быть оптимизировано) , Это означает, что добавление ref
добавляет указатель. Итак, ref-of-a-ref ссылочного типа является указателем на указатель на указатель? Каждый дополнительный ref
добавляет уровень косвенности?
Для целей этой дискуссии мы не заботимся, если это действительно адрес или что-то другое. Поскольку объекты могут перемещаться в памяти, мы понимаем, что это, вероятно, не адрес виртуальной памяти хост-машины/операционной системы, но это адрес в широком смысле.
Некоторые делают различия между ссылками и указателями, последние представляют собой числовые адреса памяти, но другие не боятся называть эти указатели тоже, потому что они указывают на объекты. Например, спецификация языка Java (§4.3.1) прямо говорит:
Эталонные значения (часто просто ссылка) являются указатели на эти объекты, а также специальная ссылка нулевой, которая относится ни к какому объекту.
Они подчеркивают «ссылки» - новый термин, который они вводят, но какие указатели очевидны для читателя, по их мнению, и там тоже объекты не являются неотъемлемыми AFAIK.
«Я понимаю, что C# ref позволяет отправлять указатель на объект». - это не то, что это значит ... когда вы назначаете 'var obj = GetSomeObject();' - *, который * присваивает ссылку (которая является * по существу * fancy-talk для указателя) объекту –
exampleObject уже является указатель. Он указывает на объект, хранящийся в куче. Поэтому 'ref' дает вам указатель на указатель. Эта деталь, которая использовалась для заполнения половины класса программирования 101 до конца семестра. Нет «массива указателей», указатель, хранящийся в exampleObject, достаточен для нахождения членов. –
@Hans Passant: Я получаю это, но если нет массива указателей, как можно ссылаться на все свойства объекта? Нужно знать места их памяти, чтобы изменить их ценности, не так ли? – Kai