Почему я не могу инициализировать переменные 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/...