Можно ли использовать 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 ключевое слово, они автоматически обрабатываются таким образом.

Другие вопросы по тегам