Ошибка компиляции. Использование свойств со структурой

Пожалуйста, объясните следующую ошибку конструктора struct. Если я сменил struct на класс эры исчезли.

public struct DealImportRequest
{
    public DealRequestBase DealReq { get; set; }
    public int ImportRetryCounter { get; set; }

    public DealImportRequest(DealRequestBase drb)
    {
        DealReq = drb;
        ImportRetryCounter = 0;
    }
}
  • ошибка CS0188: объект 'this' не может использоваться до того, как все его поля будут присвоены
  • ошибка CS0843: поле резервного копирования для автоматически реализуемого свойства "DealImportRequest.DealReq" должен быть полностью назначен, прежде чем управление будет возвращено вызывающему. Рассмотрим вызов конструктора по умолчанию из инициализатора конструктора.

Ответ 1

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

public DealImportRequest(DealRequestBase drb) : this()
{
   DealReq = drb;
   ImportRetryCounter = 0;
}

Из спецификации языка:

10.7.3 Автоматически реализованные свойства

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

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

Обратите внимание, что структура, которую у вас есть, изменена. Это не рекомендуется. Я предлагаю вам либо сделать тип классом (ваши проблемы с компиляцией должны немедленно уйти), либо сделать тип неизменным. Самый простой способ сделать это, предполагая, что код, который вы представили, является всей структурой, заключался бы в том, чтобы сделать сеттеры частными (get; private set;). Конечно, вы также должны убедиться, что впоследствии не добавляете какие-либо методы мутации в структуру, которые полагаются на частный доступ для изменения полей. Кроме того, вы можете вернуть свойства с полями readonly и полностью избавиться от сеттеров.

Ответ 2

Код, который у вас есть, эквивалентен следующему коду:

public struct DealImportRequest
{
    private DealRequestBase _dr;
    private int _irc;
    public DealRequestBase DealReq
    {
      get { return _dr; }
      set { _dr = value; }
    }
    public int ImportRetryCounter
    {
      get { return _irc; }
      set { _irc = value; }
    }
    /* Note we aren't allowed to do this explicitly - this is didactic code only and isn't allowed for real*/
    public DealImportRequest()
    {
        this._dr = default(DealRequestBase); // i.e. null or default depending on whether this is reference or value type.
        this._irc = default(int); // i.e. 0
    }
    public DealImportRequest(DealRequestBase drb)
    {
        this.DealReq = drb;
        this.ImportRetryCounter = 0;
    }
}

Теперь все, что я сделал, это удалить синтаксический сахар, который:

  • Реализует автоматические свойства.
  • Разрабатывает, с какими членами обрабатывается относительно this.
  • Дает все struct конструктор no-parameter по умолчанию.

Первые два являются необязательными (вы можете написать их явно, если хотите), но третье - нет, нам не разрешено писать собственный код для конструктора без параметров без struct, мы должны пойти с тем, который работает как тот, который указан в приведенном выше коде, предоставляется нам автоматически.

Теперь, посмотрев здесь, внезапно становится понятным смысл этих двух ошибок: ваш конструктор неявно использует this, прежде чем ему будут присвоены поля (ошибка 188), и эти поля являются теми, которые поддерживают автоматические свойства (ошибка 843).

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

public DealImportRequest(DealRequestBase drb)
    :this()
{
    DealReq = drb;
    ImportRetryCounter = 0;
}

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

Ответ 3

Я бы рекомендовал не использовать авто-свойства со структурами, если у вас нет веских оснований для их использования. Объединение поля класса в свойство read-write полезно, поскольку оно позволяет экземпляру контролировать условия, в которых он может быть прочитан или написан, и принимать меры при чтении или записи. Кроме того, код внутри экземпляра объекта может идентифицировать исполняемый экземпляр и, таким образом, может выполнять специальное действие только при чтении и записи конкретного экземпляра. Использование авто-свойства в ранней версии класса позволит будущим версиям класса использовать свойство, реализованное вручную, включая вышеупомянутые преимущества, сохраняя при этом совместимость с уже скомпилированным клиентским кодом. К сожалению, обертка поля структуры в свойстве чтения и записи не дает те же преимущества, потому что поля одного экземпляра структуры могут быть скопированы в другой, без какого-либо экземпляра, имеющего какое-либо мнение в этом вопросе. Если семантика структуры допускает, чтобы свойство было записано с произвольными значениями в большинстве случаев [как это было бы в случае авто-свойства], то любая законная замена была бы семантически эквивалентна полю.