Почему FxCop считает, что инициализировать поля значениями по умолчанию - это плохо?

При назначении значения по умолчанию для поля (здесь 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. Это не только мое мнение, но и то, чему меня учили в университете (Бельгия). Не то чтобы мне нравилось использовать argumentum ad verecundiam, но просто чтобы показать, что это не только мое мнение. А по поводу этого:

  2. Приношу свои извинения, я только что нашел вот это:

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


person Peter    schedule 08.04.2009    source источник
comment
Я не думаю, что от отказа от инициализации есть хоть какой-то выигрыш в производительности.   -  person R. Martinho Fernandes    schedule 08.04.2009
comment
Я согласен с Мартиньо, но мне нравится ваш вопрос!   -  person Jay Riggs    schedule 08.04.2009
comment
@Martinho Fernandes: Отказ от этого дает преимущество в производительности - см. Мой ответ и указанную статью.   -  person Reed Copsey    schedule 08.04.2009
comment
@ Рид: Спасибо, что опровергли меня. Я обычно не инициализирую код по умолчанию с определенной целью, которую вы указываете в своем ответе, но всегда полезно знать, что есть преимущество в производительности.   -  person R. Martinho Fernandes    schedule 08.04.2009
comment
я уверен, что компилятор удаляет ненужные инициализации.   -  person Ian Boyd    schedule 06.07.2009
comment
@ R.MartinhoFernandes Вы были правы в том, что отсутствие инициализации не принесет никакого выигрыша в производительности. Тест, на который ссылается Рид Копси, был неверным. Смотрите мой ответ ниже.   -  person Daniel A.A. Pelsmaeker    schedule 02.04.2013


Ответы (6)


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

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

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

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

person Reed Copsey    schedule 08.04.2009
comment
ненужное для кого или для чего? Вы, конечно, кодируете людей, для машины комментарии тоже не нужны, это не значит, что они не нужны. - person Peter; 08.04.2009
comment
Вы можете добавить его в качестве комментария так же просто, как утверждение, и это не окажет такого же влияния на производительность. Это похоже на то, что код недоступен - он может что-то предоставить вам, но я бы предпочел оставить его в качестве комментария, чем оставлять недостижимый код в моих методах. - person Reed Copsey; 08.04.2009
comment
Разница с комментарием заключается в том, что я говорю int i = 0; Я хочу, чтобы i был равен нулю, утверждение логично. Даже при компиляции с будущим компилятором, у которого есть другие значения по умолчанию (маловероятно, конечно, для int) - person Peter; 08.04.2009
comment
И вопрос: если вы хотите, чтобы дата была инициализирована 1/1/1, вы опускаете это значение, потому что оно не нужно, и вы серьезно думаете, что это было бы хорошей идеей? - person Peter; 08.04.2009
comment
@Peter: вам гарантируется, что в спецификации CLR для int всегда будет по умолчанию установлено значение 0 - это не проблема компилятора, а проблема времени выполнения / платформы. Если вы хотите, чтобы дата была инициализирована значением, отличным от значения по умолчанию, обязательно инициализируйте ее, но только если вы используете значение, отличное от значения по умолчанию. - person Reed Copsey; 08.04.2009
comment
atReed (мой at не будет работать для некоторого reasen :)), но с датой 1/1/1 оказывается по умолчанию, опуская это не кажется ясным или хорошей практикой, если вы имеете в виду эту (действительно историческую) дату . - person Peter; 08.04.2009
comment
@Peter: это частная переменная-член. В этом случае (что является своего рода крайним случаем) я, вероятно, сделаю комментарий / замечание, что мне действительно нужна эта дата, и все же устраню ее. Однако FxCop не будет жаловаться, если вы укажете дату как = new DateTime (1,1,0001); - person Reed Copsey; 08.04.2009
comment
... Поскольку это не конструктор по умолчанию. Он будет жаловаться, только если вы сделаете date = new DateTime (); - person Reed Copsey; 08.04.2009
comment
@Peter Я понимаю вашу точку зрения, но в этом случае ваше значение по умолчанию должно быть константой (или статическим значением только для чтения), чтобы сделать его более явным. int i = DEFAULT_WHATEVER; или DateTime d = DEFAULT_BIRTH_DATE; вместо int i = 0; и DateTime d = новый DateTime (1, 0, 1); - person Michael Meadows; 08.04.2009
comment
atReed: Дополнительный код, даже если он был безвредным, предполагает, что он существует по какой-то причине, что снижает ремонтопригодность. - ›это не случайно, конечно, для ясности и для непредвиденного использования в будущем. Широко распространенные ненужные фигурные скобки после if существуют для того же - person Peter; 08.04.2009
comment
причина if (test) { doIt(); } у Майкла: tx - person Peter; 08.04.2009
comment
Фигурные скобки предназначены для удобства чтения, но не влияют на скомпилированный IL. Однако инициализатор изменяет поведение вашего кода во время выполнения. Вы можете отключить это предупреждение в FxCop, однако я согласен с этим (хотя мне потребовалось много времени, чтобы с ним согласиться ...) - person Reed Copsey; 08.04.2009
comment
@ReedCopsey Я перепроверил тесты, и очевидно, что тесты неправильные (и всегда были). Разницы в производительности нет. Я разместил свои результаты ниже. - person Daniel A.A. Pelsmaeker; 02.04.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 # правильно оптимизирует тот случай, когда вы назначаете значения по умолчанию для полей. Никакой разницы в производительности!


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

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

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

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

person Daniel A.A. Pelsmaeker    schedule 02.04.2013

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
person Michael Meadows    schedule 08.04.2009
comment
tx (также предлагалось в предыдущем комментарии) - person Peter; 08.04.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 не жаловаться на это.

person Bob The Janitor    schedule 08.04.2009
comment
ненужный код, конечно, плох, но этот код может стать необходимым в том маловероятном случае, если в будущем значение по умолчанию будет изменено, и этот код будет перекомпилирован. - person Peter; 08.04.2009
comment
@Peter: значения по умолчанию для типов значений всегда инициализируются нулями. Это часть самой спецификации CLR. Изменить тип значения по умолчанию невозможно. Типы ссылок такие же - согласно спецификации они гарантированно имеют значение по умолчанию null. - person Reed Copsey; 08.04.2009
comment
@ Боб Дворник, делать вещи, которые кажутся неправильными, просто чтобы удовлетворить инструмент, обычно заканчивается плохо! ;) - person Michael Meadows; 08.04.2009
comment
он не делает что-то, чтобы удовлетворить инструмент, я намеренно установил правило, которое его применяет. Если вы посмотрите на XML-документ, в примечаниях говорится, что когда что-то было документами, это должно быть в то же время, когда оно было создано, а затем помещает, когда что-то было создано, в нашу документацию. - person Bob The Janitor; 08.04.2009

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

person James Curran    schedule 08.04.2009
comment
совершенно разные вопросы imo, (между () есть DateTime по умолчанию, и каково его значение? Что делает x--? Я думаю, что ответ на последнее знают 100% программистов, ответ на первый - если мы ' повторно повезло - 50%) - person Peter; 08.04.2009
comment
Во-первых, это значение по умолчанию (DateTime). Во-вторых, при каких обстоятельствах вам будет важна реальная стоимость? - person James Curran; 09.04.2009

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

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

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

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

person casperOne    schedule 08.04.2009