Подтвердить что ты не робот

Почему я НЕ получаю предупреждения о неинициализированных полях readonly?

Компилятор С# достаточно добр, чтобы дать вам предупреждение "поле никогда не назначается", если вы забыли инициализировать член readonly, который является частным или внутренним, или если класс, в котором он объявляется, является внутренним. Но если класс является общедоступным, а член readonly является общедоступным, защищенным или защищенным внутренним, то для вас нет предупреждений!

Кто-нибудь знает, почему?

Пример кода, который демонстрирует условия, при которых выдается предупреждение, и условия, при которых предупреждение не выдается:

namespace Test1 
{ 
    class Test1
    { 
#if TRY_IT 
        public readonly int m; //OK: warning CS0649: Field is never assigned to, and will always have its default value 0 
        protected readonly int n; //OK: warning CS0649: Field is never assigned to, and will always have its default value 0 
        internal readonly int o; //OK: warning CS0649: Field is never assigned to, and will always have its default value 0 
        private readonly int p; //OK: warning CS0649: Field is never assigned to, and will always have its default value 0 
        protected internal readonly int q; //OK: warning CS0649: Field is never assigned to, and will always have its default value 0 

        Test1()
        {
            if( p != 0 ) //To avoid warning 'The field is never used'
                return;
        }
#endif
    } 

    public class Test2
    { 
#if TRY_IT 
        private readonly int m; //OK: warning CS0649: Field is never assigned to, and will always have its default value 0 
        internal readonly int n; //OK: warning CS0649: Field is never assigned to, and will always have its default value 0 

        Test2()
        {
            if( m != 0 ) //To avoid warning 'The field is never used'
                return;
        }
#endif 
        public readonly int o; //Blooper: no warning about field never assigned to. 
        protected readonly int p; //Blooper: no warning about field never assigned to. 
        protected internal readonly int q; //Blooper: no warning about field never assigned to.
    } 

    public sealed class Test3
    { 
        public readonly int m; //Blooper: no warning about field never assigned to. 
    } 
} 

EDIT: На мгновение вы можете подумать, что компилятор воздерживается от выдачи предупреждения в случае публичных и защищенных членов, потому что разумно ожидать, что производные классы могут инициализировать это поле. Эта теория не содержит воды по ряду причин:

  • Внутренний класс может быть подклассом, но компилятор не воздерживаться от выдачи предупреждения в этом случае.

  • Компилятор не может выдать предупреждение даже в случае запечатанного класс, как демонстрирует Test3 в примере кода.

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

  • Класс явно запрещен языком при инициализации readonly член базового класса. (Спасибо, Джим Мишель.)

EDIT2: Если моя память мне хорошо помогает, Java дает все правильные предупреждения во всех случаях, независимо от того, является ли неинициализированный конечный элемент общедоступным, защищенным или закрытым и независимо от того, содержит ли класс, содержащий он является общедоступным или видимым только внутри его пакета.

4b9b3361

Ответ 1

Короткий ответ: это надзор в компиляторе.

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

Я расскажу об этом Нилу в новом году, и мы увидим, можем ли мы улучшить эти эвристики в Рослине.

Кстати, существует ряд ситуаций, в которых может быть выпущено предупреждение такого рода (независимо от чтения только), но мы этого не делаем. Сегодня я не в своем офисе, поэтому у меня нет списка всех этих ситуаций, но достаточно сказать, что их много. Это было нечто вроде "поле объявлено как общедоступное и находится в публичном вложенном классе внутреннего класса". В этой ситуации поле эффективно внутреннее, и мы можем сделать предупреждение, но иногда мы этого не делаем.

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

Ответ 2

Это Документация MSDN: предупреждение компилятора (уровень 4) CS0649:

Поле 'field' никогда не назначается и всегда будет иметь значение по умолчанию value 'value'

Компилятор обнаружил неинициализированное личное или внутреннее поле объявление, которому никогда не присваивается значение.

Итак, для не-внутренних и нефайловых полей вы не должны ожидать предупреждения.

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

Но я тестировал protected internal, и я не знаю, почему компилятор С# не предупреждает об этом.

Ответ 3

Если он является общедоступным (или лучше не закрытым), он может использоваться другим классом вне вашего проекта. Это важно для создания библиотек, которые должны использоваться другими. Если вы получите предупреждение для каждого неиспользуемого открытого свойства, поля или метода, вы не увидите реальных проблем.

Ответ 4

Я думаю, это потому, что предупреждение связано с объемом и использованием. Доступ к общедоступному и защищенному члену можно получить за пределами области сборки и потребителем класса, и разработчику действительно может потребоваться, чтобы значение переменной было по умолчанию.

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