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

Posted

技术标签:

【中文标题】我应该总是/永远/从不将对象字段初始化为默认值吗?【英文标题】:Should I always/ever/never initialize object fields to default values? 【发布时间】:2009-03-11 19:59:52 【问题描述】:

此处的代码样式问题。

我查看了this 问题,该问题询问 .NET CLR 是否真的总是会初始化字段值。 (答案是是的。)但令我震惊的是,我不确定让它这样做总是一个好主意。我的想法是,如果我看到这样的声明:

int myBlorgleCount = 0;

我有一个很好的想法,程序员希望计数从零开始,并且可以接受,至少在不久的将来。另一方面,如果我只是看到:

int myBlorgleCount;

我不知道 0 是否是合法或合理的值。如果程序员只是开始阅读和修改它,我不知道程序员是否打算在为其设置值之前开始使用它,或者他们是否期望它为零,等等。

另一方面,一些相当聪明的人和 Visual Studio 代码清理实用程序告诉我删除这些多余的声明。对此的普遍共识是什么? (有共识吗?)

我将其标记为与语言无关,但如果有一个奇怪的案例,即针对特定语言违背常规是一个特别好的主意,那可能值得指出。

编辑:虽然我确实说过这个问题与语言无关,但它显然不适用于像 C 这样没有进行值初始化的语言。

编辑:我很欣赏约翰的回答,但这正是我要寻找的。我了解 .NET(或 Java 或其他)将完成这项工作并一致且正确地初始化值。我要说的是,如果我看到代码正在修改以前没有在代码中明确设置的值,我作为代码维护者,不知道原始编码器是否意味着它是默认值,或者只是忘记设置值,或者期望将其设置在其他地方,等等。

【问题讨论】:

【参考方案1】:

考虑长期维护。

使代码尽可能明确。 如果不需要,不要依赖特定于语言的初始化方式。也许新版本的语言会有所不同? 未来的程序员会感谢你。 管理层会感谢你的。 为什么要稍微混淆一下?

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

【讨论】:

对于未来的维护者:他们问的第一天,告诉他们他们所处的世界。问题结束。而且我们不是在谈论语言功能。这是一个平台功能。对于每一种 .NET 语言来说都是如此,而且永远都是如此。 想想如果这种行为改变会发生什么! 这不是重点,不是吗?不是所有的软件都是.NET?还是我生活在平行宇宙中? John:维护者可能完全理解这个问题,但是他们怎么知道编码器在他们没有初始化值的情况下想要获取默认值?也许它应该是默认值,但也许不是。毕竟,如果代码的一切都很完美,他们就不会维护它。 @Subtwo:我希望原始程序员的意图得到自动化测试和人工 QA 的确认。这样,如果程序员未初始化该字段,但打算将其初始化为其他内容,我们就会知道并继续了解它。否则,假设最初的程序员和他的经理以及那些进行代码审查的人共同知道他们生活在一个字段总是被初始化为其默认值的世界。 @John Saunders:嗯...并非所有项目都遵循有关测试等方面的良好实践。再说一次 - 我真的看不出保持清晰的好处。如果你能想到一个更清楚的情况,实际上让字段未初始化,请告诉我,我想开导。【参考方案2】:

您始终可以安全地假设平台以平台的方式运行。 .NET 平台将所有字段初始化为默认值。如果你看到一个没有被代码初始化的字段,这意味着这个字段是被CLR初始化的,而不是它没有被初始化。

此问题适用于不保证初始化的平台,但此处不适用。在 .NET 中,更多的是表示开发人员的无知,认为初始化是必要的。


另一个不必要的过去后遗症如下:

string foo = null;
foo = MethodCall();

我从应该更了解的人那里看到了这一点。

【讨论】:

在 C#(可能还有 VB.NET)中,如果您尝试读取从未在代码中设置的变量,编译器将抛出错误。仅此一项就可以防止出现您描述的情况,因为变量必须始终在读取之前设置在某个位置,即使不在声明中也是如此。 您错过了在任何情况下始终设置值的点。想象可能会发生不同的事情是没有意义的。除非您与一个期望“int i;”的非常愚蠢的开发人员打交道,否则它永远不会是一个错误。初始化到 1. 解雇他,问题解决了。 @Beska:总的来说,我们总是希望我们的同事知道他们在做什么。我们测试我们的代码来证明它。代码本身不是证明这一点的地方。您不想在每个 i = 1+1 之后放置一个 Assert(i == 2)。总是如此。 @John 曾经是一名维护编码员,我可以肯定地说,知道自己在做什么的人(即使是非常优秀的人)可以忽略这样的简单错误。在某些情况下,假设他们知道自己在做什么是有害的,因为这会导致对他们行为的假设。 (cont) 在显式声明的情况下,没有假设。我确切地知道以前的编码员想要的值是什么。【参考方案3】:

我认为,如果它阐明了开发人员的意图,那么初始化这些值是有意义的。

在 C# 中,没有任何开销,因为值都已初始化。在 C/C++ 中,未初始化的值将包含垃圾/未知值(内存位置中的任何内容),因此初始化更为重要。

【讨论】:

FxCop 在 Performance 部分下有一条规则,专门调用了不必要的初始化。如果他们告诉你不要这样做,一定有一些浪费。这在大多数实际应用程序中并不重要...... 没错...我的代码检查工具也有类似的规则。但我不清楚具体原因...特别是因为我被告知(但尚未验证)它通常实际上不会造成任何浪费...冗余初始化已被优化。【参考方案4】:

如果真的有助于使代码更易于理解,我认为应该这样做。

但我认为这是所有语言功能的普遍问题。我对此的看法是:如果它是语言的官方特性,你可以使用它。(当然有一些反特性应该谨慎使用或完全避免,比如在 Visual Basic 中缺少 option explicit 或在 C++ 中缺少菱形继承)

有一段时间我非常偏执并添加了各种不必要的初始化、显式强制转换、超级偏执的 try-finally 块……我什至想过忽略自动装箱并用显式类型替换所有出现的事件转换,只是“为了安全起见”。

问题是:没有尽头。你可以避免几乎所有的语言特性,因为你不想信任它们。

记住:在你理解之前,这只是魔法:)

【讨论】:

抱歉,但我对“如果它是该语言的官方特性,你应该使用它”提出异议。 C++ 中的菱形继承是一个很好的对位,只是为了初学者。 是的,我明白你的意思,但我认为这过度解释了这句话。当然,做所有可能的事情并不总是好的。不管怎样,我把它改成了“可以”。 @NotSure:三年后再次阅读您的评论后,我注意到您所说的与我的回答无关。我的意思是:如果一种语言具有菱形继承特性,并且您希望依赖该菱形继承特性及其所有丑陋的后果,那么您不应该自己重新实现它,而应该使用现有特性。 @DanielRikowski 这不是不信任框架功能的问题。毫无疑问,无论开发人员做什么,该值都将由框架初始化。问题是为了代码清晰。如果我试图追踪某人代码中的错误,并且他们已经开始使用此变量而没有显式初始化它,那么这对我来说可能是一个危险信号;我可能认为 0 可能不是此变量的有效值,我必须阅读更多代码才能找到答案。如果程序员已将其显式设置为 0,我会立即知道“0 是一个有效值;继续前进。” @Beska:这正是我在第一句话中所说的。 :)【参考方案5】:

我同意你的看法;它可能很冗长,但我喜欢看:

int myBlorgleCount = 0;

现在,我总是初始化字符串:

string myString = string.Empty;

(我只是讨厌空字符串。)

【讨论】:

【参考方案6】:

在我无法立即将其设置为有用的情况下

int myValue = SomeMethod();

我将它设置为 0。这更多是为了避免不得不考虑否则该值会是什么。对我来说,整数总是设置为 0 的事实不在我的指尖,所以当我看到

int myValue;

我需要花一点时间来回忆这个事实并记住它将被设置为什么,从而打乱了我的思维过程。 对于那些随时掌握这些知识的人,他们会遇到

int myValue = 0;

并且想知道为什么那个人将它设置为零,而编译器只会为他们做这件事。这种想法会打断他们的思考过程。

所以,做对你和你所在团队最有意义的事情。如果通常的做法是设置它,那么就设置它,否则不要。

【讨论】:

【参考方案7】:

根据我的经验,我发现显式初始化局部变量(在 .NET 中)带来的混乱多于清晰。

另一方面,类范围的变量应该总是被初始化。过去,我们为常见变量类型定义了系统范围的自定义“null”值。这样我们总能知道什么是错误未初始化的,什么是故意初始化的。

【讨论】:

【参考方案8】:

我总是在构造函数中显式地初始化字段。对我来说,这是做这件事的地方。

【讨论】:

【参考方案9】:

我认为这很大程度上取决于过去的经历。

在较旧且未分类的语言中,期望值是未知的。来自这些语言的程序员保留了这种期望。

几乎所有现代或托管语言都为最近创建的变量定义了值,无论是来自类构造函数还是语言特性。

目前,我认为初始化一个值是非常好的;曾经隐含的东西变成了明确的。从长远来看,比如说,在接下来的 10 到 20 年内,人们可能会开始了解到默认值是可能的、预期的和已知的——尤其是如果它们在不同语言之间保持一致(例如,空字符串表示字符串,0 表示数字) .

【讨论】:

除了在某些语言中,如 Java,空字符串“”实际上是一个字符串。您应该改为将 String 变量初始化为 null,这样您就不会不必要地创建 String 对象。 是的,正是我的意思:现在,跨语言的默认设置几乎是确定的。 :) @David,在 C# 中也是如此。 (如果你写var x = "";,你刚刚创建了一个新的字符串对象。)【参考方案10】:

你应该这样做,没有必要,但最好这样做,因为你永远不知道你使用的语言是否初始化了这些值。通过自己做,您可以确保您的值已初始化并设置了标准的预定义值。 这样做并没有错,除了可能有点“浪费时间”。我强烈推荐它。虽然 John 的推荐信息量很大,但在一般用途上,最好走安全的道路。

【讨论】:

既然他在谈论 .NET,那么省略冗余的初始化安全路径,因为字段总是被初始化的。如果他不使用 .NET,那么答案取决于平台。在非托管 C/C++ 的情况下,这是必要的。【参考方案11】:

我通常对字符串和在某些情况下不希望空值浮动的集合执行此操作。 我工作的普遍共识是“不要为值类型明确地这样做。”

【讨论】:

【参考方案12】:

我不会这样做。 C# 无论如何都会将 int 初始化为零,因此这两行在功能上是等效的。一个只是更长且多余,尽管对于不懂 C# 的程序员来说更具描述性。

【讨论】:

【参考方案13】:

这被标记为与语言无关,但大多数答案都是关于 C#。

在 C 和 C++ 中,最佳做法是始终初始化您的值。在某些情况下会为您完成此操作,例如静态全局变量,但使用大多数编译器冗余初始化这些值不会对性能造成任何影响。

【讨论】:

他在询问可以保证初始化的 .net 语言。 嗯,不一定是 .NET,而是可以保证某种默认初始化的任何语言。问题是:尽管有这个保证,是否应该专门初始化该值?【参考方案14】:

我不会初始化它们。如果您使声明尽可能接近第一次使用,那么就不应该有任何混淆。

【讨论】:

【参考方案15】:

要记住的另一件事是,如果要使用自动属性,则必须依赖隐式值,例如:

public int Count  get; set; 

【讨论】:

【参考方案16】:

http://www.geekherocomic.com/2009/07/27/common-pitfalls-initialize-your-variables/

【讨论】:

【参考方案17】:

如果一个字段通常会在其中存储新值而不考虑之前的内容,并且如果它应该表现得好像最初存储了一个零但零没有什么“特殊”,那么应该存储该值明确的。

如果该字段表示一个计数或总计,它永远不会直接写入非零值,而是始终添加或减去其他数量,那么零应该被视为“空”值,因此需要没有明确说明。

使用粗略的类比,请考虑以下两个条件:

    `if (xposition != 0) ... `if ((flags & WoozleModes.deluxe) != 0) ...

在前一种情况下,与文字零进行比较是有意义的,因为它正在检查一个在语义上与任何其他位置没有区别的位置。然而,在第二种情况下,我建议与文字零的比较不会增加可读性,因为代码并不真正关心表达式 (flags & WoozleModes.deluxe) 的值是否恰好是零以外的数字,而是是否它是“非空的”。

我不知道有任何编程语言提供不同的方法来区分“零”和“空”的数值,除了在指示空时不需要使用文字零。

【讨论】:

以上是关于我应该总是/永远/从不将对象字段初始化为默认值吗?的主要内容,如果未能解决你的问题,请参考以下文章

构造器

将 XML 序列化为对象

将字节数组初始化为某个值,而不是默认的null? [复制]

Java基础之类的基础

如何将所有字段都是默认值的类型反序列化为 None ?

C# 变量初始化问题