Можно ли использовать ref как постоянную связь между классами?
У меня есть один экземпляр ClassA, который передается как ссылка на конструктор ClassB. Всегда ли экземпляр ClassB теперь имеет доступ к новейшей обновленной версии переданного экземпляра ClassA?
public class ClassA {
private int variableA = 0;
public ClassA() { }
public void Change(int newValue) {
variableA = newValue;
}
}
public class ClassB {
public ClassA classA;
public ClassB(ref ClassA refClassA) {
classA = refClassA;
}
public void Print() {
Console.WriteLine(classA.variableA);
}
}
static void Main() {
ClassA classA = new ClassA();
ClassB classB = new ClassB(ref classA);
classB.Print(); // 0
classA.Change(50);
classB.Print(); // 50?
}
Я прочитал то, что нашел в Интернете, но единственное использование, которое я нашел, было обновить ссылочное значение, как в dotnetperls http://www.dotnetperls.com/ref.
6 ответов
Вам не нужно ключевое слово ref здесь. Даже без этого ClassB будет содержать ссылку на ClassA, а не копию его значений.
Обратите внимание, что ваш код не будет работать, потому что переменная A является закрытой, поэтому ClassB не может получить к ней доступ в своем Print
метод. Однако, кроме этого, результат будет таким, как вы ожидали в вашем примере
Это не то, что ref
для, то же самое поведение достигается без него.
Ссылка не имеет ничего общего с постоянством. Это только означает, что вы можете изменить значение исходной переменной. Если вы вызываете метод без ref, метод может присвоить эту переменную как угодно, а переменная вызывающего не изменится. С ref, вызывающая сторона также будет указывать на другое место.
Простой пример:
void Call(int x)
{
x = 2;
}
void CallRef(ref int x)
{
x = 10;
}
int a=0;
Call(a); // a is still 0
CallRef(ref a); // a is 10
Да, это так. Но это даже не нужно, поскольку объекты всегда являются указателями, а ref
Ключевое слово используется для передачи указателя не объектов в функции (например, int, float, Struct и т. д.)
Если вы хотите иметь доступ к последнему экземпляру ClassA
Вы можете обернуть свою переменную классом, подобным этому:
public class Ref<T>
{
readonly Func<T> getter;
readonly Action<T> setter;
public T Value
{
get
{
return getter();
}
set
{
setter(value);
}
}
public Ref(Func<T> getter, Action<T> setter)
{
this.getter = getter;
this.setter = setter;
}
}
Затем измените ваш ClassB
как это:
public class ClassB
{
readonly Ref<ClassA> refClassA;
public ClassA ClassA
{
get
{
return refClassA.Value;
}
}
public ClassB(Ref<ClassA> refClassA)
{
this.refClassA = refClassA;
}
public void Print()
{
Console.WriteLine(ClassA.VariableA);
}
}
Затем вы можете проверить и посмотреть, есть ли у вас желаемое поведение:
var a = new ClassA(1);
var b = new ClassB2(new Ref<ClassA>(() => a, x => a = x));
b.Print(); // output 1
var c = a; // backup old reference
a = new ClassA(2);
b.Print(); // output 2
// b stores old instance of a?
Console.WriteLine(object.ReferenceEquals(b.ClassA, c)); // output false
// a stores new instance of a?
Console.WriteLine(object.ReferenceEquals(b.ClassA, a)); // output true
Я предполагаю, что вы хотите что-то подобное, имейте в виду, что это черновик, поэтому вам придется много работать.
РЕДАКТИРОВАТЬ
Я украл Ref<T>
класс от блога Эрика Липперта, наконец я нашел этот ТАК вопрос, где он отправил это.
Да, это имеет ссылку на это. Это означает, что он не сохраняет значения элементов ClassA, а просто его адрес в памяти (в куче).
Поэтому убедитесь, что вы не восстанавливаете класс A:
classA.change(50)
будет работать но classA = new ClassA()
не буду.
Также обратите внимание, что ссылочные типы (такие как классы) не нуждаются в ref
ключевое слово, они автоматически обрабатываются таким образом.