为什么 FxCop 认为将字段初始化为默认值不好?

发布于 2024-07-16 09:33:36 字数 1084 浏览 5 评论 0原文

当为字段分配默认值时(此处为 bool 为 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. 这不仅仅是我的观点,也是我在大学所学到的 (比利时)。 并不是说我喜欢使用 争论,但是 只是为了表明这不只是我的 观点。 关于这一点:

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

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

When assigning a default default-value to a field (here false to a bool), FxCop says:

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

Now, I know that code as int a = 0 or bool ok = false is introducing some redundancy, but to me it seems a very, very good code-practice, one that my teachers insisted on righteously in my opinion.

Not only is the performance penalty very little, more importantly: relying on the default is relying on the knowledge of each programmer ever to use a piece of code, on every datatype that comes with a default. (DateTime?)

Seriously, I think this is very strange: the very program that should protect you from making all too obvious mistakes, is suggesting here to make one, only for some increased performance? (we're talking about initialization code here, only executed once! Programmers who care that much, can of course omit the initialization (and should probably use C or assembler :-) ).

Is FxCop making an obvious mistake here, or is there more to it?

Two updates :

  1. This is not just my opinion, but what I have been taught at university
    (Belgium). Not that I like to use an
    argumentum ad verecundiam, but
    just to show that it isn't just my
    opinion. And concerning that:

  2. My apologies, I just found this one:

    Should I always/ever/never initialize object fields to default values?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

傲世九天 2024-07-23 09:33:36

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

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

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

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

There can be significant performance benefits from this, in some cases. For details, see this CodeProject article.

The main issue is that it is unnecessary in C#. In C++, things are different, so many professors teach that you should always initialize. The way the objects are initialized has changed in .NET.

In .NET, objects are always initialized when constructed. If you add an initializer, it's possible (typical) that you cause a double initialization of your variables. This happens whether you initialize them in the constructor or inline.

In addition, since initialization is unnecessary in .NET (it always happens, even if you don't explicitly say to initialize to the default), adding an initializer suggests, from a readability standpoint, that you are trying to add code that has a function. Every piece of code should have a purpose, or be removed if unnecessary. The "extra" code, even if it was harmless, suggests that it is there for a reason, which reduces maintainability.

海夕 2024-07-23 09:33:36

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 编译为具有默认优化的发行版。我惊讶地发现性能仍然存在差异在使用默认值显式初始化字段和不指定默认值之间。 我本来希望后来的 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<在此测试中,/code> 和 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# 编译器确实优化了常量,而且我怀疑它总是这样做。

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

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# 编译器正确地针对以下情况进行了优化:您为字段指定默认值。 这对性能没有影响!


现在我们知道性能实际上不是一个问题。 我不同意 Reed Copsey 的声明 “‘额外’代码,即使它是无害的,也表明它存在是有原因的,这会降低可维护性。”并同意 Anders Hansson 对此:

考虑长期维护。

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

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

Reed Copsey said specifying default values for fields impacts performance, and he refers to a test from 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;
}

Now eight years and four versions of C# and .NET later I decided to repeat that test under .NET Framework 4.5 and C# 5, compiled as Release with default optimizations using Visual Studio 2012. I was surprised to see that there is still a performance difference between explicitly initializing fields with their default values and not specifying a default value. I would have expected the later C# compilers to optimize those constant assignments away by now.

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

(the rest)

So I peeked inside the constructors of classes A and B in this test, and both are empty:

.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

I could not find a reason in the IL why explicitly assigning default constant values to fields would consume more time. Apparently the C# compiler does optimize the constants away, and I suspect it always did.

So, then the test must be wrong... I swapped the contents of classes A and B, swapped the numbers in the result string, and reran the tests. And lo and behold now suddenly specifying default values is faster.

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

(the rest)

Therefore I conclude that the C# compiler correctly optimizes for the case where you assign default values to fields. It makes no performance difference!


Now we know performance is really not an issue. And I disagree with Reed Copsey's statement "The 'extra' code, even if it was harmless, suggests that it is there for a reason, which reduces maintainability." and agree with Anders Hansson on this:

Think long term maintenance.

  • Keep the code as explicit as possible.
  • Don't rely on language specific ways to initialize if you don't have to. Maybe a newer version of the language will work differently?
  • Future programmers will thank you.
  • Management will thank you.
  • Why obfuscate things even the slightest?

Future maintainers may come from a different background. It really isn't about what is "right" it is more what will be easiest in the long run.

瞳孔里扚悲伤 2024-07-23 09:33:36

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

FxCop has to provide rules for everyone, so if this rule doesn't appeal to you, just exclude it.

Now, I would suggest that if you want to explicitly declare a default value, use a constant (or static readonly variable) to do it. It will be even clearer than initializing with a value, and FXCop won't complain.

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
把回忆走一遍 2024-07-23 09:33:36

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 不要这样做抱怨它。

FX Cop sees it as adding unnecessary code and unnecessary code is bad. I agree with you, I like to see what it's set to, I think it makes it easier to read.

A similar problem we encounter is, for documentation reasons we may create an empty constructor like this

/// <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
    }        
}

The compiler creates this constructor automatically, so FX cop complains but our sandcastle documentation rules require all public methods to be documented, so we just told fx cop not to complain about it.

辞旧 2024-07-23 09:33:36

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

It's relies not on every programmer knowing some locally defined "corporate standard" which might change between at any time, but on something formally defined in the Standard. You might as well say "don't using x++ because that relies on the knowledge of every programmer.

好倦 2024-07-23 09:33:36

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

何时排除警告

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

现在,该规则并不完全错误,但正如它所说,这不是您的优先事项,因此只需禁用该规则即可。

You have to remember that FxCop rules are only guidelines, they are not unbreakable. It even says so on the page for the description of the rule you mentioned (http://msdn.microsoft.com/en-us/library/ms182274(VS.80).aspx, emphasis mine):

When to Exclude Warnings:

Exclude a warning from this rule if the constructor calls another constructor in the same or base class that initializes the field to a non-default value. It is also safe to exclude a warning from this rule, or disable the rule entirely, if performance and code maintenance are not priorities.

Now, the rule isn't entirely incorrect, but like it says, this is not a priority for you, so just disable the rule.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文