свойства класса, которые могут быть назначены только при создании объекта?

#c#

#c#

Вопрос:

В C # возможно писать вещи таким образом:

 Instrument instr = new Instrument { ClassCode = "Hello", Ticker = "World" };
  

Однако для этого вам нужно добавить set; в соответствующий класс:

 class Instrument
{
    public string ClassCode { get; set; }
    public string Ticker { get; set; }
}
  

Это означает, что позже кто-то может случайно изменить значение:

 instr.ClassCode.set = "Destroy"
  

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

 Instrument instr = new Instrument { ClassCode = "Hello", Ticker = "World" };
  

Я не уверен, возможно ли это. Вероятно, мне следует использовать поля или что-то еще вместо свойств. Я хочу иметь синтаксис только последнего предложения, но в то же время сохранить все только для чтения.

upd: Короче говоря, нет, свойства только для чтения никоим образом не разрешены. В этом случае следует использовать обычный конструктор и «get».

Комментарии:

1. Почему вы не используете конструктор? Честно говоря, синтаксис не так уж сильно отличается, если только вы не без ума от этих фигурных скобок.

2. я не использую constructor, потому что у меня уже есть много кода, который использует new Instrument { ClassCode = "Hello", Ticker = "World" }; . Я просто хочу сделать now Instrument доступным только для чтения, потому что на самом деле это доступно только для чтения…

Ответ №1:

Я бы добился этого, используя частные установщики и вызывая их из конструктора. Таким образом, вы можете контролировать, когда вызываются установщики.

 Instrument instr = new Instrument("Hello", "World");

class Instrument
{
    public Instrument(string ClassCode, string Ticker) 
    {
        this.ClassCode = ClassCode;
        this.Ticker = Ticker;
    }

    public string ClassCode { get; private set; }
    public string Ticker { get; private set; }
}
  

Ответ №2:

Это происходит потому, что код, который вы используете:

 Instrument instr = new Instrument { ClassCode = "Hello", Ticker = "World" };
  

это просто синтаксический сахар для этого:

 Instrument instr = new Instrument();
instr.ClassCode = "Hello";
instr.Ticker = "World";
  

Два примера выше абсолютно одинаковы, первый является просто сокращением для последнего.

Для достижения этой функциональности вы хотите сделать эти значения закрытыми. Что-то вроде этого:

 class Instrument
{
  private string _classCode;
  private string _ticker;
  public string ClassCode{ get { return _classCode; } }
  public string Ticker{ get { return _ticker; } }
  private ClassCode() {}
  public ClassCode(string classCode, string ticker)
  {
    _classCode = classCode;
    _ticker = ticker;
  }
}
  

Что у вас здесь есть:

  • Частные значения, которые могут быть установлены только внутри класса, таким образом, они не могут быть переопределены кем-либо другим позже.
  • Общедоступные свойства, доступные только для чтения, для чтения этих значений.
  • Частный конструктор по умолчанию, чтобы никто не мог создать экземпляр этого объекта без указания необходимых значений.
  • Общедоступный конструктор, который требует необходимых значений.

Вы бы создали его экземпляр следующим образом:

 Instrument instr = new Instrument("Hello", "World");
  

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

Ответ №3:

Если вы склоняетесь к синтаксису инициализатора, типичный способ сделать это — создать объекты «эскимо», объекты, которые могут быть плавными для начала, но затем вы замораживаете их, и изменения блокируются.

В принципе, я бы создал класс следующим образом (протестируйте это с помощью LINQPad):

 void Main()
{
    Instrument instr1 = new Instrument
    {
        ClassCode = "Hello",
        Ticker = "World"
    };
    instr1.ClassCode = "123";              // is allowed

    Instrument instr2 = new Instrument
    {
        ClassCode = "Hello",
        Ticker = "World"
    }.Freeze();                            // <-- notice Freeze here
    instr2.ClassCode = "123";              // throws InvalidOperationException
}

public class Instrument
{
    private string _ClassCode;
    private string _Ticker;
    private bool _IsFrozen;

    public string ClassCode
    {
        get { return _ClassCode; }
        set
        {
            ThrowIfFrozen();
            _ClassCode = value;
        }
    }

    public string Ticker
    {
        get { return _Ticker; }
        set
        {
            ThrowIfFrozen();
            _Ticker = value;
        }
    }

    private void ThrowIfFrozen()
    {
        if (_IsFrozen)
            throw new InvalidOperationException(
                "Instrument object has been frozen");
    }

    public Instrument Freeze()
    {
        _IsFrozen = true;
        return this;
    }

    public bool IsFrozen
    {
        get
        {
            return _IsFrozen;
        }
    }
}
  

Комментарии:

1. спасибо, это интересно. Однако сообщение об ошибке будет получено во время выполнения, а не во время разработки. Я думаю, что лучше все-таки использовать constructor.

Ответ №4:

Предполагая, что вы устанавливаете значение только через конструктор, вы могли бы явно написать метод set, чтобы проверить, является ли поле, которому он присваивается, неназначенным (null или что-то еще), и записать значение в set только в том случае, если оно неназначено. Таким образом, ваш набор фактически доступен для записи один раз. Большую часть пути это проделало бы там.

 class Instrument
{
     private string _classCode;
     private string _ticker;

     public string ClassCode
     {
        get
        {
          return _classCode;
         }
        set
        {
           if (_classCode == null)
              _classCode = value;
        }
      }
     public string Ticker     {
        get
        {
          return _ticker;
         }
        set
        {
           if (_ticker == null)
              _ticker = value;
        }
      }


}
  

Комментарии:

1. это может привести к странным проблемам, если кто-то напишет «.set (..)», он ожидает, что затем будет установлено новое значение…

Ответ №5:

Одним из стандартных решений является принятие начальных значений ваших свойств в конструкторе — что-то вроде этого:

 class Instrument
{
  public string ClassCode {get; private set}
  public Instrument (string classCode)
  {
    this.ClassCode = classCode;
  }
}
  

Ответ №6:

Ну, вроде как возможно 🙂

сделайте set закрытым и создайте общедоступный конструктор, чтобы вы могли передавать ClassCode и Ticker, т.Е.:

 class Instrument
{
    public string ClassCode { get; private set; }
    public string Ticker { get; private set; }

    public Instrument(ClassCode classCode, Ticker ticker)
    {
         ClassCode = classCode;
         Ticker = ticker
    }
}
  

Ответ №7:

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

 class Instrument {
    public Instrument(string ClassCode = null, string Ticker = null)
    {
        this.ClassCode = ClassCode;
        this.Ticker = Ticker;
    }
    public string ClassCode { get; private set; } 
    public string Ticker { get; private set; } 
}
  

затем вы можете создавать объекты в своем коде следующим образом:

 Instrument instrument = new Instrument(ClassCode: "classCode", Ticker: "ticker");
  

Ответ №8:

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

Если ваш класс является

 class Instrument
{

    private string _ClassCode;
    private string _Ticker;

    public Instrument(string ClassCode, string Ticker)
{
_ClassCode = ClassCode;
_Ticker = Ticker;
}
    public string ClassCode { get {return _ClassCode;}}
    public string Ticker { get {return _Ticker;} }
}
  

вы получите желаемый эффект.

или

Вы можете создавать отдельные свойства с разными уровнями доступа, например:

 private string _ClassCode;
      internal string ClassCodeSet
      {
         set { _ClassCode = value; }
      }
      public string ClassCode
      {
         get { return _ClassCode; }
      }
  

Затем используйте дружественный класс для инициализации значения (согласно вашему исходному коду) и общедоступное свойство для чтения значения.

Ответ №9:

Добавьте конструктор и придайте им свойства, возвращающие переменную:

 class Instrument
{
    private string classCode;
    private string ticker;
    Instrument(string classCode, string ticker)
    {
        this.classCode = classCode;
        this.ticker = ticker;
    }
    public string ClassCode
    {
         get { return classCode; }
    }
    public string Ticker
    {
          get { return ticker; }
    }
}