2010-12-16 2 views
13

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

class Foo 
{ 
    public readonly int bar; 
} 

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

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

EDIT

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

+3

После вашего последнего редактирования я понятия не имею, о чем вы спрашиваете. Да, «readonly» применяется во время выполнения CLR. Я не вижу, как это может быть ограничение компилятора. Другие ответы объясняют, почему то, что вы пытаетесь сделать, не имеет никакого смысла. – 2010-12-16 19:09:57

+0

@Cody Grey - * язык ограничение *, нет сборщик ограничение. Компилятор просто реализует язык. – 2010-12-17 01:40:43

+0

@pst: Я сказал: «Я не вижу, как это может быть ограничение компилятора». Я с тобой согласен. – 2010-12-17 01:43:15

ответ

9

Разрешающая установка 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(); 

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

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

1

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

5

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

16

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

new Foo { bar=0; }; 

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

new Foo { bar=0 } 

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

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

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

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

Имейте в виду, что существует два этапа «компиляции». Он применяется при компиляции кода C# в код IL, и он применяется при компиляции кода IL в машинный код.

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

3

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

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

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

0

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

0

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

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

2

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

class Foo 
    { 
     public readonly int bar; 

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

    Foo x = new Foo(0); 
-1

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

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

То, что вы ищете и, вероятно, найдено в то же время, называется аргументами: https://stackoverflow.com/a/21273185/2712726 Это не то, что вы просили, но приближаетесь.

Кроме того, чтобы быть справедливым, я должен добавить, что есть очень хорошо осведомленные авторы, которые полностью не согласятся с этими взглядами на константную корректность, которую часто имеют разработчики C++. Эрик Липперт, который, по общему признанию, имеет блестящие должности, написал это (ужасающее для разработчиков C++) заявление: https://stackoverflow.com/a/3266579/2712726

Смежные вопросы