Как я присваиваюсь “ссылкой” на поле класса в c#?

Я пытаюсь понять, как присвоиться "ссылкой" на поле класса в c#.

У меня есть следующий пример для рассмотрения:

 public class X
 {

  public X()
  {

   string example = "X";

   new Y( ref example );

   new Z( ref example );

   System.Diagnostics.Debug.WriteLine( example );

  }

 }

 public class Y
 {

  public Y( ref string example )
  {
   example += " (Updated By Y)";
  }

 }

 public class Z
 {

  private string _Example;

  public Z( ref string example )
  {

   this._Example = example;

   this._Example += " (Updated By Z)";

  }

 }

 var x = new X();

При выполнении вышеупомянутого кода вывод:

X (обновленный Y)

И нет:

X (обновленный Y) (обновленный Z)

Поскольку я надеялся.

Кажется, что присвоение "касательно параметра" к полю теряет ссылку.

Там какой-либо путь состоит в том, чтобы держаться за ссылку при присвоении полю?

Спасибо.

27
задан Jamie 5 June 2010 в 13:37
поделиться

3 ответа

Нет. ref - это чисто соглашение о вызовах. Вы не можете использовать его для квалификации поля. В Z для _Example устанавливается значение переданной строковой ссылки. Затем вы назначаете ему новую строковую ссылку, используя + =. Вы никогда не назначаете example, поэтому ссылка не имеет никакого эффекта.

Единственный обходной путь для того, что вы хотите, - это иметь общий изменяемый объект-оболочку (массив или гипотетический StringWrapper), который содержит ссылку (здесь строка). Как правило, если вам это нужно, вы можете найти более крупный изменяемый объект для общих классов.

 public class StringWrapper
 {
   public string s;
   public StringWrapper(string s)
   {
     this.s = s;
   }

   public string ToString()
   {
     return s;
   }
 }

 public class X
 {
  public X()
  {
   StringWrapper example = new StringWrapper("X");
   new Z(example)
   System.Diagnostics.Debug.WriteLine( example );
  }
 }

 public class Z
 {
  private StringWrapper _Example;
  public Z( StringWrapper example )
  {
   this._Example = example;
   this._Example.s += " (Updated By Z)";
  }
 }
9
ответ дан 28 November 2019 в 04:18
поделиться

Как отмечали другие, у вас не может быть поля типа «ссылка на переменную». Однако просто знать, что вы не можете этого сделать, вероятно, неудовлетворительно; вы, вероятно, также захотите узнать, во-первых, почему бы и нет, а во-вторых, как обойти это ограничение.

Причина в том, что есть только три возможности:

1) Запретить поля типа ссылки

2) Разрешить небезопасные поля типа ссылки

3) Не использовать пул временного хранения для локальных переменных (он же «стек»)

Предположим, мы разрешили поля типа ref. Затем вы можете выполнить

public ref int x;
void M()
{
    int y = 123;
    this.x = ref y;
}

, и теперь y можно будет получить после завершения M . Это означает, что либо мы в случае (2) - доступ к this.x приведет к сбою и ужасной смерти, потому что хранилище для y больше не существует, либо мы в случае (3), и локальный y хранится в куче со сборкой мусора, а не во временном пуле памяти.

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

Обратите внимание, что для локальных переменных, которые являются замкнутыми переменными анонимных функций, мы выбираем вариант (3); эти локальные переменные не выделяются из временного пула.

Это подводит нас ко второму вопросу: как это обойти? Если вам нужно поле ref, чтобы создать геттер и сеттер другой переменной, это совершенно законно:

sealed class Ref<T>
{
    private readonly Func<T> getter;
    private readonly Action<T> setter;
    public Ref(Func<T> getter, Action<T> setter)
    {
        this.getter = getter;
        this.setter = setter;
    }
    public T Value { get { return getter(); } set { setter(value); } }
}
...
Ref<int> x;
void M()
{
    int y = 123;
    x = new Ref<int>(()=>y, z=>{y=z;});
    x.Value = 456;
    Console.WriteLine(y); // 456 -- setting x.Value changes y.
}

И готово. y хранится в куче gc, а x - это объект, который может получать и устанавливать y .

Обратите внимание, что среда CLR поддерживает методы возврата ref locals и ref, а C # - нет.Возможно, гипотетическая будущая версия C # будет поддерживать эти функции; Я создал его прототип, и он работает хорошо. Однако это не так уж и важно в списке приоритетов, так что я не возлагал надежд.

ОБНОВЛЕНИЕ: функция, упомянутая в предыдущем абзаце, наконец-то была реально реализована в C # 7. Однако вы по-прежнему не можете сохранить ссылку в поле.

70
ответ дан 28 November 2019 в 04:18
поделиться

Вы забыли обновить ссылку в классе Z:

public class Z {
    private string _Example;

    public Z(ref string example) {
        example = this._Example += " (Updated By Z)";
    }
}

Вывод: X (Обновлено Y) (Обновлено Z)

Следует помнить, что оператор + = для строки вызывает String .Concat () метод. Что создает новый строковый объект , но не обновляет значение строки. Строковые объекты неизменяемы, строковый класс не имеет методов или полей, позволяющих изменять значение. Очень отличается от поведения по умолчанию для обычного ссылочного типа.

Таким образом, если вы используете строковый метод или оператор, вы всегда должны присваивать возвращаемое значение переменной. Это довольно естественный синтаксис, типы значений ведут себя точно так же. Ваш код был бы очень похож, если бы вы использовали int вместо строки.

3
ответ дан 28 November 2019 в 04:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: