Назначения свойств в инициализаторах объектов не рассматриваются на том же уровне, что и авто-свойства в C# 6
В C#6 появилась возможность инициализировать свойства без установщика, так что теперь можно использовать такой синтаксис
public class MyClass
{
public int Answer { get; } = 42;
}
или даже это
public class MyClass
{
public int Answer { get; }
public MyClass()
{
Answer = 42;
}
}
Я знаю (точнее, сильно предполагаю), что это будет переведено в сгенерированный readonly
поле с методом доступа в CIL, поэтому я понимаю, как это
public class MyClass
{
public int Answer { get; }
public MyClass CreateMyClassInstance()
{
return new MyClass()
{
Answer = 42
};
}
}
не компилируется (поскольку технически присваивание происходит вне конструктора, что противоречит ограничениям, налагаемым поддержкой readonly
поле).
Мой вопрос: почему такое поведение запрещено в первую очередь? Почему, с точки зрения синтаксиса и / или компиляции, назначения свойств, являющиеся частью инициализатора объекта, не просто рассматриваются как дополнительная встроенная логика для выполнения после, но все еще в конструкторе объекта? Это по замыслу, результат технических ограничений или обратной совместимости, или, может быть, просто изменение, которое недостаточно важно для рассмотрения?
2 ответа
Почему такое поведение запрещено в первую очередь?
Разрешение свойства только для чтения назначаться из инициализатора объекта нарушает инкапсуляцию. Любая инициализация, выполненная в конструкторе, может быть позже переопределена клиентским кодом в инициализаторе объекта. Для классов было бы невозможно держать свои инварианты.
Такая функция не только не нужна, но и опасна.
Почему, с точки зрения синтаксиса и / или компиляции, назначения свойств, являющиеся частью инициализатора объекта, не просто рассматриваются как дополнительная встроенная логика для выполнения после, но все еще в конструкторе объекта?
Это будет означать, что для каждого инициализатора объекта компилятор должен будет сгенерировать новый конструктор. Возможно изменение и нарушение типа из другой сборки.
Или newobj
Инструкция CIL должна быть изменена, чтобы позволить выполнение некоторого произвольного кода после конструктора.
В дополнение к тому, что сказал Джекуб Лортц, лучший способ добиться желаемого эффекта - использовать Answer { get; private set }
или в C# 6 вы можете назначить readonly
свойство при инициализации выглядит так:
int Answer { get; } = 42;
куда Answer
является readonly
С тех пор. Я бы принял значение, присвоенное Answer
должен быть константой. Если нет, вы могли бы вытащить некоторые взломать.
Редактировать:
Вы можете изменить свой объект так:
public class MyClass
{
private bool readOnly;
private int x;
public int X
{
get { return x; }
set
{
if (readOnly) throw new InvalidOperationException();
else x = value;
}
}
public MyClass()
{
X = 42;
readOnly = true;
}
}
Это проверено и работает. В этом коде readOnly может быть изменен в любой точке типа, что позволяет вам изменять X. Вы можете расширить эту идею, хотя, где вы создаете это в object
, interface
, или же generic
тип, который имеет метод x.MakeReadOnly();
и не имеет возможности отменить это, и любая попытка сделать это приведет к ошибке.