24

为字段分配默认默认值时(此处为布尔值 false),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 = 0bool ok = false正在引入一些冗余,但对我来说,这似乎是一种非常非常好的代码实践,我的老师在我看来是正确坚持的。

不仅性能损失很小,更重要的是:依赖默认值是依赖于每个程序员曾经使用过一段代码的知识,依赖于每个带有默认值的数据类型。(约会时间?)

说真的,我觉得这很奇怪:应该保护你不犯太明显错误的程序,是建议在这里做一个,只是为了提高性能?(我们在这里谈论的是初始化代码,只执行一次!那么关心的程序员当然可以省略初始化(并且可能应该使用 C 或汇编程序:-))。

FxCop 在这里犯了一个明显的错误,还是有更多的错误?

两个更新:

  1. 这不仅仅是我的观点,而是我在大学(比利时)所教的。并不是说我喜欢使用 argumentum ad verecundiam,只是为了表明这不仅仅是我的观点。关于这一点:

  2. 抱歉,我刚找到这个:

    我应该总是/永远/从不将对象字段初始化为默认值吗?

4

6 回答 6

11

在某些情况下,这可能会带来显着的性能优势。有关详细信息,请参阅此 CodeProject 文章。

主要问题是它在 C# 中是不必要的。在 C++ 中,情况有所不同,因此许多教授都教导您应该始终初始化。初始化对象的方式在 .NET 中发生了变化。

在 .NET 中,对象总是在构造时被初始化。如果添加初始化程序,则可能(典型地)导致变量的双重初始化。无论您是在构造函数中还是内联中初始化它们,都会发生这种情况。

此外,由于在 .NET 中初始化是不必要的(它总是会发生,即使您没有明确表示要初始化为默认值),从可读性的角度来看,添加初始化程序表明您正在尝试添加具有功能。每一段代码都应该有一个目的,或者在不必要的时候被删除。“额外”代码,即使它是无害的,也表明它的存在是有原因的,这会降低可维护性。

于 2009-04-08T17:26:41.860 回答
6

Reed Copsey 说为字段指定默认值会影响性能,他指的是 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 下重复该测试,使用 Visual Studio 2012 编译为具有默认优化的 Release。我惊讶地发现仍然存在性能差异在使用默认值显式初始化字段和不指定默认值之间。我本来希望后来的 C# 编译器现在可以优化那些常量赋值。

无初始化:512,75 默认初始化:536,96 常量初始化:720,50
方法无初始化:140,11 Def 方法初始化:166,86

其余

所以我在类的构造函数AB这个测试中偷看了一下,两者都是空的:

.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# 编译器确实优化了常量,我怀疑它总是这样做的。

所以,那么测试一定是错误的......我交换了 classesA和的内容B,交换了结果字符串中的数字,然后重新运行了测试。你瞧,现在突然指定默认值更快了。

无初始化:474,75 默认初始化:464,42 常量初始化:715,49
方法无初始化:141,60 方法初始化在 Def:171,78

其余

因此,我得出结论,C# 编译器正确优化了您为字段分配默认值的情况。它没有性能差异!


现在我们知道性能真的不是问题。我不同意Reed Copsey 的说法‘额外’代码,即使它是无害的,表明它的存在是有原因的,这会降低可维护性。 ”并同意Anders Hansson的观点:

考虑长期维护。

  • 保持代码尽可能明确。
  • 如果不需要,不要依赖语言特定的初始化方式。也许新版本的语言会以不同的方式工作?
  • 未来的程序员会感谢你。
  • 管理层会感谢你的。
  • 为什么要混淆事物,哪怕是最轻微的?

未来的维护者可能来自不同的背景。这真的不是关于什么是“正确的”,而是从长远来看最容易的事情。

于 2013-04-02T14:55:43.657 回答
5

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
于 2009-04-08T18:09:30.087 回答
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 会抱怨,但我们的沙堡文档规则要求记录所有公共方法,所以我们只是告诉 fx cop 不要抱怨它。

于 2009-04-08T17:32:15.513 回答
2

它不依赖于每个程序员都知道一些本地定义的“公司标准”,这些标准可能随时变化,而是依赖于标准中正式定义的东西。您不妨说“不要使用x++,因为这依赖于每个程序员的知识。

于 2009-04-08T17:28:00.530 回答
2

您必须记住,FxCop 规则只是指导方针,它们并非牢不可破。它甚至在您提到的规则描述的页面上这么说(http://msdn.microsoft.com/en-us/library/ms182274(VS.80).aspx,强调我的):

何时排除警告

如果构造函数调用相同或基类中的另一个构造函数,该构造函数将字段初始化为非默认值,则从该规则中排除警告。如果性能和代码维护不是优先事项,则从该规则中排除警告或完全禁用该规则也是安全的

现在,该规则并非完全不正确,但正如它所说,这不是您的优先事项,所以只需禁用该规则。

于 2009-04-08T17:31:56.637 回答