Инициализатор поля не может ссылаться на нестатическое поле, метод или свойство

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

using System;
using System.Collections.Generic;
using System.Linq;

namespace MySite
{
    public class Reminders
    {
        public Dictionary<TimeSpan, string> TimeSpanText { get; set; }

        // We are setting the default values using the Costructor
        public Reminders()
        {
            TimeSpanText.Add(TimeSpan.Zero, "None");
            TimeSpanText.Add(new TimeSpan(0, 0, 5, 0), "5 minutes before");
            TimeSpanText.Add(new TimeSpan(0, 0, 15, 0), "15 minutes before");
            TimeSpanText.Add(new TimeSpan(0, 0, 30, 0), "30 minutes before");
            TimeSpanText.Add(new TimeSpan(0, 1, 0, 0), "1 hour before");
            TimeSpanText.Add(new TimeSpan(0, 2, 0, 0), "2 hours before");
            TimeSpanText.Add(new TimeSpan(1, 0, 0, 0), "1 day before");
            TimeSpanText.Add(new TimeSpan(2, 0, 0, 0), "2 day before");
        }

    }
}

Использование класса в другом классе

class SomeOtherClass
{  
    private Reminders reminder = new Reminders();
    // error happens on this line:
    private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)]; 
    ....

Ошибка (CS0236):

A field initializer cannot reference the nonstatic field, method, or property

Почему это происходит и как его исправить?

Ответ 1

Эта строка:

private dynamic defaultReminder = 
                          reminder.TimeSpanText[TimeSpan.FromMinutes(15)];

Вы не можете использовать переменную экземпляра для инициализации другой переменной экземпляра. Зачем? Поскольку компилятор может их изменить, нет гарантии, что reminder будет инициализирован до defaultReminder, поэтому вышеприведенная строка может вызвать NullReferenceException.

Вместо этого просто используйте:

private dynamic defaultReminder = TimeSpan.FromMinutes(15);

Альтернативно, настройте значение в конструкторе:

private dynamic defaultReminder;

public Reminders()
{
    defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)]; 
}

Более подробная информация об этой ошибке компилятора в MSDN - Ошибка компилятора CS0236.

Ответ 2

Вам нужно поместить этот код в конструктор вашего класса:

private Reminders reminder = new Reminders();
private dynamic defaultReminder;

public YourClass()
{
    defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)];
}

Причина в том, что вы не можете использовать одну переменную экземпляра для инициализации другой с помощью инициализатора поля.

Ответ 3

вы можете использовать это как

private dynamic defaultReminder => reminder.TimeSpanText[TimeSpan.FromMinutes(15)]; 

Ответ 4

private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)]; является инициализатором поля и выполняется первым (до того, как любое поле без инициализатора будет установлено в его значение по умолчанию, и до того, как будет выполнен вызванный конструктор экземпляра). Поля экземпляра, которые не имеют инициализатора, будут иметь допустимое (по умолчанию) значение только после завершения всех инициализаторов полей экземпляра. Из-за порядка инициализации конструкторы экземпляров выполняются последними, поэтому экземпляр не создается в тот момент, когда выполняются инициализаторы. Поэтому компилятор не может допустить ссылки на какое-либо свойство экземпляра (или поле) до того, как экземпляр класса будет полностью создан. Это связано с тем, что любой доступ к переменной экземпляра, например reminder, неявно ссылается на экземпляр (this), чтобы сообщить компилятору конкретное место в памяти экземпляра для использования.

Это также причина, почему this не разрешен в инициализаторе поля экземпляра.

Инициализатор переменной для поля экземпляра не может ссылаться на экземпляр создается. Таким образом, это ошибка времени компиляции для ссылки это в инициализаторе переменной, так как это ошибка времени компиляции для инициализатор переменной для ссылки на любой элемент экземпляра через простое-.

Единственные члены типа, которые гарантированно инициализируются перед выполнением инициализаторов поля экземпляра, - это инициализаторы поля класса (статические) и конструкторы класса (статические) и методы класса. Поскольку на статические элементы ссылаются экземпляры независимо, на них можно ссылаться в любое время:

class SomeOtherClass
{
  private static Reminders reminder = new Reminders();

  // This operation is allowed,
  // since the compiler can guarantee that the referenced class member is already initialized
  // when this instance field initializer executes
  private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)];
}

Вот почему инициализаторам полей экземпляров разрешено ссылаться только на член класса (статический член). Эти правила инициализации компилятора обеспечат детерминированную реализацию типа.

Для получения более подробной информации я рекомендую этот документ: Документы Microsoft: объявления классов.

Это означает, что поле экземпляра, которое ссылается на другой элемент экземпляра для инициализации его значения, должно быть инициализировано из конструктора экземпляра, или указанный элемент должен быть объявлен static.