Почему FxCop считает, что инициализация полей к значению по умолчанию - это плохо? - PullRequest
24 голосов
/ 08 апреля 2009

При назначении значения по умолчанию для поля (здесь false для bool), FxCop говорит:

Resolution   : "'Bar.Bar()' initializes field 'Bar.foo' 
               of type 'bool' to false. Remove this initialization 
               because it will be done automatically by the runtime."

Теперь я знаю, что код как int a = 0 или bool ok = false вводит некоторую избыточность, но мне кажется, что это очень, очень хорошая практика кодирования, на которой мои учителя, по моему мнению, справедливо настаивали.

Мало того, что снижение производительности очень мало, что еще более важно: полагаться на значение по умолчанию - значит полагаться на знания каждого программиста, когда-либо использующего фрагмент кода, на каждом типе данных, который поставляется с значением по умолчанию. (DateTime?)

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

FxCop делает очевидную ошибку здесь, или есть что-то еще?

Два обновления:

  1. Это не только мое мнение, но и то, чему меня учили в университете (Бельгия). Не то, чтобы я хотел использовать argumentsum ad verecundiam , но просто чтобы показать, что это не только мой мнение. И относительно этого:

  2. Мои извинения, я только что нашел это:

    Должен ли я всегда / никогда / никогда не инициализировать поля объекта значениями по умолчанию?

Ответы [ 6 ]

12 голосов
/ 08 апреля 2009

В некоторых случаях это может принести значительный выигрыш в производительности. Подробнее см. В этой статье CodeProject.

Основная проблема в том, что это не нужно в C #. В C ++ все по-другому, поэтому многие преподаватели учат, что вам всегда следует инициализироваться. Способ инициализации объектов изменился в .NET.

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

Кроме того, поскольку в .NET инициализация не нужна (это всегда происходит, даже если вы явно не говорите инициализировать по умолчанию), добавление инициализатора предполагает, с точки зрения читабельности, что вы пытаетесь добавить код это имеет функцию. Каждый фрагмент кода должен иметь цель или быть удален, если в этом нет необходимости. «Дополнительный» код, даже если он был безвредным, предполагает, что он существует по причине, которая снижает удобство обслуживания.

5 голосов
/ 02 апреля 2013

Рид Копси сказал , указав значения по умолчанию для полей, влияющих на производительность, и он ссылается на тест 2005 года .

public class A
{
    // Use CLR's default value
    private int varA;
    private string varB;
    private DataSet varC;
}

public class B
{
    // Specify default value explicitly
    private int varA = 0;
    private string varB = null;
    private DataSet varC = null;
}

Теперь, спустя восемь лет и четыре версии C # и .NET, позже я решил повторить этот тест под .NET Framework 4.5 и C # 5, скомпилированный как Release с оптимизацией по умолчанию с помощью Visual Studio 2012. Я был удивлен, увидев, что все еще есть разница в производительности между явной инициализацией полей их значениями по умолчанию и отсутствием указания значения по умолчанию. Я бы ожидал, что более поздние компиляторы C # уже оптимизируют эти константы.

No init: 512,75    Init on Def: 536,96    Init in Const: 720,50
Method No init: 140,11    Method Init on Def: 166,86

( остальные )

Итак, я заглянул внутрь конструкторов классов A и B в этом тесте, и оба пусты:

.method public hidebysig specialname rtspecialname 
    instance void .ctor () cil managed 
{
    // Code size 7 (0x7)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: call instance void [mscorlib]System.Object::.ctor()
    IL_0006: ret
} // end of method .ctor

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

Итак, тогда тест должен быть неправильным ... Я поменял местами классы A и B, поменял числа в строке результата и перезапустил тесты. И о чудо теперь внезапно задание значений по умолчанию быстрее .

No init: 474,75    Init on Def: 464,42    Init in Const: 715,49
Method No init: 141,60    Method Init on Def: 171,78

( остальные )

Поэтому я делаю вывод, что компилятор C # правильно оптимизирует для случая, когда вы назначаете значения по умолчанию для полей. Нет никакой разницы в производительности!


Теперь мы знаем, что производительность действительно , а не проблема. И я не согласен с заявлением Рида Копси"" Дополнительный "код, даже если он безвреден, предполагает, что он существует по причине, которая снижает удобство обслуживания. ", и согласен с Андерс Ханссон на это:

Подумайте о долгосрочном обслуживании.

  • Сохраняйте код как можно более явным.
  • Не полагайтесь на специфичные для языка способы инициализации, если это не нужно. Может быть, более новая версия языка будет работать по-другому?
  • Будущие программисты будут вам благодарны.
  • Менеджмент поблагодарит вас.
  • Зачем запутывать вещи даже малейшим?

Будущие сопровождающие могут прийти из другого фона. Дело не в том, что «правильно», а в том, что будет легче в долгосрочной перспективе.

5 голосов
/ 08 апреля 2009

FxCop должен предоставить правила для всех, поэтому, если это правило вам не подходит, просто исключите его.

Теперь я хотел бы предложить, чтобы, если вы хотите явно объявить значение по умолчанию, используйте для этого константу (или статическую переменную только для чтения). Это будет даже понятнее, чем инициализация значением, и FXCop не будет жаловаться.

private const int DEFAULT_AGE = 0;

private int age = 0; // FXCop complains
private int age = DEFAULT_AGE; // FXCop is happy

private static readonly DateTime DEFAULT_BIRTH_DATE = default(DateTime);

private DateTime birthDate = default(DateTime); // FXCop doesn't complain, but it isn't as readable as below
private DateTime birthDate = DEFAULT_BIRTH_DATE; // Everyone's happy
3 голосов
/ 08 апреля 2009

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

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

/// <summary>
/// a test class
/// </summary>
/// <remarks>Documented on 4/8/2009  by richard</remarks>
public class TestClass
{
    /// <summary>
    /// Initializes a new instance of the <see cref="TestClass"/> class.
    /// </summary>
    /// <remarks>Documented on 4/8/2009  by Bob</remarks>
    public TestClass()
    {
        //empty constructor
    }        
}

Компилятор создает этот конструктор автоматически, поэтому FX cop жалуется, но наши правила документирования sandcastle требуют документирования всех открытых методов, поэтому мы просто сказали fx cop не жаловаться на это.

2 голосов
/ 08 апреля 2009

Вы должны помнить, что правила FxCop являются только рекомендациями, они не являются неразрывными. Это даже говорит на странице описания правила, которое вы упомянули (http://msdn.microsoft.com/en-us/library/ms182274(VS.80).aspx, выделение мое):

Когда исключать предупреждения :

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

Теперь правило не совсем неверно, но, как говорится, это не является для вас приоритетом, поэтому просто отключите правило.

2 голосов
/ 08 апреля 2009

Это зависит не от того, что каждый программист знает какой-то локально определенный «корпоративный стандарт», который может измениться в любое время, а от того, что формально определено в Стандарте. С тем же успехом можно сказать: «Не используйте x++, потому что это зависит от знаний каждого программиста.

...