Почему я НЕ получаю предупреждения о неинициализированных полях только для чтения? - PullRequest
21 голосов
/ 31 декабря 2011

Компилятор C # достаточно любезен, чтобы дать вам предупреждение «поле никогда не назначается», если вы забыли инициализировать элемент 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 в примере кода.

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

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

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

Ответы [ 4 ]

24 голосов
/ 31 декабря 2011

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

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

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

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

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

4 голосов
/ 31 декабря 2011

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

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

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

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

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

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

1 голос
/ 31 декабря 2011

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

0 голосов
/ 31 декабря 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...