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

Почему 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 очевидной ошибкой здесь или есть еще что-то?

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

4b9b3361

Ответ 1

В некоторых случаях могут быть значительные преимущества в производительности. Для получения дополнительной информации см. Эту статью CodeProject.

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

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

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

Ответ 2

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

Теперь я бы предположил, что если вы хотите явно объявить значение по умолчанию, используйте константу (или статическую переменную readonly), чтобы сделать это. Это будет еще более ясным, чем инициализация со значением, и 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 happy

Ответ 3

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

public class A
{
    // Use CLR 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;
}

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

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, почему явное назначение значений констант по умолчанию для полей будет потреблять больше времени. По-видимому, компилятор С# оптимизирует константы, и я подозреваю, что это всегда делалось.

Итак, тогда тест должен быть неправильным... Я поменял содержимое классов 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

(остальное)

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


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

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

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

Будущие сопровождающие могут происходить из другого фона. Речь идет не о том, что "правильно", а в том, что будет проще всего в конечном итоге.

Ответ 4

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 жалуется, но наши правила документации в Sandcastl требуют, чтобы все общедоступные методы были задокументированы, поэтому мы просто сказали fx cop не жаловаться на это.

Ответ 5

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

Ответ 6

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

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

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

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