Почему я все еще могу получить доступ к свойству формы после закрытия и обнуления формы? - PullRequest
0 голосов
/ 20 февраля 2019

У меня есть MainForm и UserConfigForm, и я использовал шаблон из этот ответ для UserConfigForm т.е.

private static UserConfigForm openForm = null;

public static UserConfigForm GetInstance() 
{
    if (openForm == null)
    {
        openForm = new UserConfigForm();
        openForm.FormClosed += delegate { openForm = null; };
    }
    return openForm;
}

Внутри UserConfigForm У меня также есть авто-property UserHasSaved т.е.

public bool UserHasSaved { get; private set; }

Теперь в MainForm мне нужно проверить, нужно ли перезагрузить пользовательскую конфигурацию, когда форма конфигурации закрыта.Так что в MainForm у меня есть

private UserConfigForm userCfgForm;

private void OpenEditFormClick(object sender, EventArgs e)
{
    userCfgForm = UserConfigForm.GetInstance();
    userCfgForm.FormClosed += ConfigFormClosed;
    userCfgForm.Show();
{

private void ConfigFormClosed(object sender, FormClosedEventArgs e)
{
    if (userCfgForm.UserHasSaved)
    {
        MessageBox.Show(message, caption);
        //Reload config
    }
}

Проблема в том, что это работает, но я не понимаю, почему это работает.У меня есть два обработчика событий, зарегистрированных на FormClosed, поэтому я решил, что было бы разумно проверить, в каком порядке обрабатываются обработчики событий.

Кажется, что обработчики событий обрабатываются в том порядке, в котором они зарегистрированы.Так что не имеет смысла, что я могу получить доступ к userCfgForm.UserHasSaved после delegate { openForm = null }.

Должен ли я беспокоиться об этом или просто быть счастливым, что это работает?

1 Ответ

0 голосов
/ 20 февраля 2019

У вас есть распространенное заблуждение новичка о том, как работают переменные ссылочного типа в C #.

Давайте рассмотрим более простой пример:

class F1
{
  static int x = 0;  
  public static int Start()
  {
    x = 1;
    return x;
  }
  public static void Stop()
  {
    x = 0;
  }
}

class F2
{
  int y;
  void DoIt()
  {
    this.y = F1.Start();
    F1.Stop();
    Console.WriteLine(this.y);
  }
}

Предположим, мы вызываем DoIt дляэкземпляр F2.Какое значение this.y?

Трассировка в результате действия программы:

  • F1.x начинается с нуля.
  • F2.DoIt вызывает F1.Start ()
  • F1.x становится 1
  • F1.Start () возвращает 1
  • F2.y становится 1
  • F2.DoIt вызывает F1.Stop ()
  • F1.x становится 0

F2.y по-прежнему 1. Изменение F2.x ничего не изменило в F2.y.Это совершенно другая переменная.Мы не создали никакой магической связи, которая говорила бы: «Когда вы читаете F2.y, действительно читаете текущее значение F2.x».

То же самое верно и в вашей программе.Мы можем изменить его на ссылочные типы, и ничего не изменится:

class F1
{
  public static F1 x = null;  
  public static F1 Start()
  {
    x = new F1();
    return x;
  }
  public static void Stop()
  {
    x = null;
  }
}

class F2
{
  F1 y;
  void DoIt()
  {
    this.y = F1.Start();
    F1.Stop();
    Console.WriteLine(this.y == null); // false
  }
}

Что происходит?То же самое.

  • F1.x начинается с нуля.
  • F2.DoIt вызывает F1.Start ()
  • F1.x становится ссылкой на действительный объект
  • F1.Start () возвращает ссылку на действительный объект
  • F2.y становится ссылкой на действительный объект
  • F2.DoIt вызывает F1.Stop ()
  • F1.x становится нулевым

Что такое F2.y? Все еще ссылка на действительный объект .F2.y был никогда ссылкой на F1.x.Они оба были ссылкой на один и тот же действительный объект. Ссылки являются значениями, как целые числа .

Теперь, если вы хотите сделать псевдоним для переменной, C # 7 позволяет вам сделать это:

class F1
{
  static int x = 0;  
  public static ref int Start()
  {
    x = 1;
    return ref x;
  }
  public static void Stop()
  {
    x = 0;
  }
}

class F2
{
  void DoIt()
  {
    ref int y = ref F1.Start();
    F1.Stop();
    Console.WriteLine(y); // 0
  }
}

* ref означает, что локальная переменная y является псевдонимом переменной F1.x , поэтому, когда мы изменяем F2.x, мы также меняем y, потому что их всего два имена для той же переменной ;они псевдоним .

Обратите внимание, что псевдоним переменной не имеет ничего общего с типом переменной .Вы можете сделать псевдоним для переменной int, вы можете сделать псевдоним для переменной объекта, что угодно.Переменная с псевдонимом и локальный псевдоним должны иметь точно такой же тип (упражнение: почему?), Но этот тип может быть любым, чем вы хотите.

Хотя правило таково: переменная с псевдонимом может быть любой переменной;переменная псевдонимов может быть только локальным или формальным параметром .Например, нет способа создать поле ref int.Обратите внимание, что ref int y является локальным , а не полем .

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...