Почему я не могу инициализировать переменные readonly в инициализаторе?

Почему я не могу инициализировать переменные readonly в инициализаторе? Не работает следующее:

class Foo
{
    public readonly int bar;
}

new Foo { bar=0; }; // does not work

Это связано с некоторыми техническими пределами CLR?

ИЗМЕНИТЬ

Я знаю, что new Foo { bar=0; } совпадает с new Foo().bar=0;, но "readonly" применяется CLR, или это просто ограничение компилятора?

Ответы

Ответ 1

Разрешающая установка readonly в инициализаторе вводит противоречия и осложнения, которые невозможно выполнить во время компиляции. Я полагаю, что ограничение состоит в том, чтобы избежать двусмысленности. Большой ключ - это проверка времени компиляции.

Представьте себе следующее:

class Foo
{
    public readonly int bar;
    Foo () {
      // compiler can ensure that bar is set in an invoked ctor
      bar = 0;
    }
}

// compiler COULD know that `bar` was set in ctor
// and therefore this is invalid
new Foo { bar = 0; }

Теперь рассмотрим:

class Foo
{
    public readonly int bar;
    Foo () {
      // imagine case where bar not set in ctor
    }
}

// compiler COULD know that `bar` is not bound yet
// therefore, this COULD be valid
new Foo { bar = 0; }

// but this COULD be proved to never be valid
new Foo();

Представьте, что оба вышеупомянутых случая унифицированы (скажем, "магией компилятора" ), однако, введите в generics:

T G<T> () where T : new
{
  // What in heck should happen *at compile time*?
  // (Consider both cases above.)
  // What happens if T (Foo) changes to include/not-include setting the
  // readonly variable in the ctor?
  // Consider intermediate code that invokes G<Foo>() and this other
  // code is NOT recompiled even though Foo is--
  //   Yet a binary incompatibility has been added!
  //   No thanks!
  return new T();
}
G<Foo>();

Я считаю, что случаи, которые я изложил, показывают некоторые сложности использования "динамического" подхода readonly, и, в конце дня, я считаю, что это просто выбранное языковое ограничение (компиляторы реализуют языки) для обеспечения/разрешения проверка времени компиляции.

Ответ 2

Инициализатор - это просто синтаксический сахар. Когда вы пишете:

new Foo { bar=0; };

(Который, кстати, является синтаксической ошибкой и должен быть этим...)

new Foo { bar=0 }

что происходит на самом деле:

var x = new Foo();
x.bar = 0;

Поскольку свойство доступно только для чтения, этот второй оператор недействителен.

Изменить: Основываясь на вашем редактировании, вопрос немного неясен. A readonly свойство, по своему усмотрению, не может быть установлено. Он строился при строительстве объекта. Это обеспечивается как компилятором, так и временем выполнения. (По общему признанию, я не тестировал последнее, так как для обхода первого понадобилось бы обмануть.)

Имейте в виду, что есть две стадии "компиляции". Он применяется при компиляции кода С# в код IL и применяется при компиляции кода IL в машинный код.

Это не технический предел CLR, и он работает точно так, как должен, учитывая явное объявление readonly. После создания объекта вы не можете установить свойство readonly.

Ответ 3

Так как переменные readonly должны быть инициализированы в конструкторе, а инициализаторы свойств выполняются после построения объекта, это недопустимо.

Ответ 4

Поскольку инициализатор эквивалентен

var foo = new Foo();
foo.bar=0;

Это переписывание за кулисами.

Ответ 5

Что вы пытаетесь сделать, так это:

   class Foo
   {
        public readonly int bar;

        Foo(int b)
        {
             bar = b;  // readonly assignments only in constructor
        }
   }

   Foo x = new Foo(0);

Ответ 6

Потому что вы указали, что он только для чтения. Не имеет смысла указывать, что что-то читается только тогда, ожидая, что оператор записи будет работать.

Ответ 7

В соответствии с эта страница, CLR по умолчанию - сначала создает объект перед обработкой списка инициализаторов, и поэтому вы назначаете bar дважды (один раз при построении по умолчанию, один раз при обработке инициализатора).

Ответ 8

Это результат реализации ключевого слова readonly. Ниже приведена цитата из справки MSDN для readonly:

Ключевое слово readonly - это модификатор, который можно использовать для полей. Когда объявление поля включает модификатор readonly, присваивания полям, введенным декларацией, могут возникать только как часть объявления или в конструкторе того же класса.

Ответ 9

Я знаю, что это не прямой ответ на вопрос автора, но более новая версия С# теперь позволяет выполнять прямую инициализацию из самого свойства следующим образом.

public int bar { get; set; } = 0;

Опять же, я знаю, что это не решает проблему только для чтения, как указано (и решено) выше.

Ответ 10

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

Если вы не утверждаете, что спецификации правильны по определению, я считаю, что здесь спецификация языка неверна. Это частично нарушает важную особенность языка, который является понятием readonly. Проблемы двусмысленности, упомянутые в других ответах, имеют, на мой взгляд, одну единственную причину. Readonly - очень навязчивая функция, и на полпути относительно корректности состязания трудно получить правильное и, что более важно, вредное для стилей кодирования.

То, что вы ищете и, вероятно, найдено в то же время, называется аргументами: fooobar.com/info/394888/... Это не то, о чем вы просили, но приближаетесь.

И, честно говоря, я должен добавить, что есть очень хорошо осведомленные авторы, которые полностью не согласятся с этими взглядами на корректность const, которые часто имеют разработчики С++. Эрик Липперт, который, по общему признанию, имеет блестящие сообщения, написал это (ужасающее для разработчиков С++) утверждение: fooobar.com/info/75098/...