Назначения свойств в инициализаторах объектов не рассматриваются на том же уровне, что и авто-свойства в 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();и не имеет возможности отменить это, и любая попытка сделать это приведет к ошибке.

Другие вопросы по тегам