#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" };
. Я просто хочу сделать nowInstrument
доступным только для чтения, потому что на самом деле это доступно только для чтения…
Ответ №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; }
}
}