为什么要在变量使用位置附近声明变量?


10

我听说有人说变量应该声明得尽可能接近其用法。我不明白

例如,此政策建议我应该这样做:

foreach (var item in veryLongList) {
  int whereShouldIBeDeclared = item.Id;
  //...
}

但是可以肯定的是,这意味着int每次迭代都会产生创建新的开销。使用会不会更好:

int whereShouldIBeDeclared;
foreach (var item in veryLongList) {
  whereShouldIBeDeclared = item.Id;
  //...
}

请有人能解释一下吗?


3
那将是两种非常不同的对待方式,这简直是该死的可怜的语言。
Paul Tomblin,

5
您是从错误的前提出发。请参阅我的回答这个问题:stackoverflow.com/questions/6919655/...
CesarGon

8
如果您这样想,就不适合进行优化,甚至不考虑总体上对性能的影响。语言实现很聪明,如果您认为不是,请使用通过公正,现实的基准获取的硬数据来证明这一点。

4
如果两个代码示例在语义上有显着差异,则它们将执行不同的操作。您应该使用可以做您想做的事。关于声明变量的位置的规则仅适用于在语义上没有区别的情况。
大卫·史瓦兹

4
考虑规模的另一端-一切都是全局变量。当然,“宣告接近使用”是这个范围的更好端吗?
JBR威尔金森2011年

Answers:


27

这是许多样式中的一种样式规则,不一定是您可以考虑的所有可能规则中最重要的规则。您的示例(由于包含int)并不是超级引人注目,但您肯定可以在该循环内构造一个昂贵的对象,并且可能在循环外构造该对象是一个很好的论据。但是,这并不是一个反对该规则的好理由,因为首先,它可以应用很多其他地方,而不涉及在循环中构造昂贵的对象,其次,一个好的优化器(并且您已经标记了C#,因此您有一个不错的优化器)可以将初始化提升到循环之外。

此规则的真正原因也是您看不到为什么这是规则的原因。人们过去常常编写数百行甚至数千行的函数,并且他们过去常常在纯文本编辑器(例如记事本)中编写函数,而没有提供Visual Studio提供的支持。在这种环境中,声明与使用位置相距数百行的变量意味着该人正在阅读

if (flag) limit += factor;

没有关于标志,限制和因素是什么的线索。诸如匈牙利符号之类的命名约定被用来帮助解决这一问题,诸如声明事物在其使用位置附近的规则也被采用。当然,如今,所有这些都与重构有关,并且函数通常不到一页长,因此很难在声明的位置和使用的位置之间取得很大的距离。您在0到20的范围内进行操作,并且怀疑在这种特定情况下7没问题,而制定规则的人会喜欢离开7条线,并试图让某人从700下降。最重要的是,在Visual Studio中,您可以将鼠标悬停在任何内容上并查看其类型,它是否是成员变量等等。这意味着减少了查看声明它的行的需要。

这仍然是一个相当不错的规则,如今确实很难打破这一规则,而且从来没有人提倡将其作为编写慢速代码的理由。首先要明智。


感谢您的回答。但是,无论数据类型如何,无论执行哪种方式,每次迭代都会创建一个新实例?只是在第二种情况下,我们并不每次都要求新的内存引用。还是我错过了重点?而且您是说C#优化器在编译时会自动改善我的代码吗?我不知道!
詹姆斯

2
创建一个int的开销很小。如果您要构造复杂的东西,那么开销会更大。
凯特·格雷戈里

17
这不仅是能够看到其类型的问题。这也是一生的问题。如果变量“ wibble”在首次使用前被声明了30行,那么在30行中错误地使用“ wibble”可能会导致错误。如果在使用前立即声明它,则在前30行中使用“摆动”不会导致错误。它将导致编译器错误。
Mike Sherrill'Cat Recall'

在这种情况下,不会在每个循环中创建一个新实例。创建单个顶级变量,并将其用于每次迭代(请参见IL)。但这是一个实现细节。
thecoop 2011年

“在Visual Studio中,您可以将鼠标悬停在任何地方都可以看到”等。还有“导航到定义”,它具有F12必不可少的快捷方式。
StuperUser 2011年

15

在循环内定义变量使其仅在该循环本地可见。对于读者而言,这至少具有3个优点:

  1. 变量定义和任何相关注释都很容易找到
  2. 读者知道,此变量从未在其他地方使用(没有依赖关系)
  3. 在编写或编辑代码时,您不可能在循环外使用相同的变量名来引用该变量,否则可能会出错。

至于效率位,编译器很聪明,可以在生成的优化代码中在循环之外生成定义。不会在每次循环迭代时创建该变量。


4

人们说尽可能接近其用法,不是说您应该一直这样做,因为在某些情况下,声明变量在最小作用域内会导致一些开销。该语句的主要原因是可读性和提供变量可以的最小范围。


4

尽管它有助于提高可读性,但在这种情况下,可读性不是主要考虑因素,现代IDE并没有消除对此规则的需求。

主要问题是未初始化的变量。如果您声明的变量与初始化之间的距离太远,它将使您面临各种潜在问题。您可能会发现自己不小心处理了RAM中以前发生的任何事情,或者该函数的计算结果更高,或者有人为了防止编译器抱怨而进行的虚拟初始化(例如0)。人们会在您的声明和用法之间插入代码,而不会意识到该变量的隐式前提。在最坏的情况下,这种用法只会在您的测试中起作用,而在现场却会失败。

在尽可能小的范围内声明变量,并在声明时将其初始化为适当的值将避免很多维护难题。它强制提高了可读性的事实只是一个不错的副作用。


1

它不是“必须”。这只是一种意见,我想做点什么。例如,我喜欢在方法的第一行中声明所有var,因此我可以注释一下该var的作用(当然,除非它们是计数器)。正如您所听到的,其他人喜欢将它们放置在尽可能接近其用法的位置(如您所写的第二个示例中所示)。无论如何,您提供的第一个示例肯定是“错误”(在您所理解的意义上,它将导致开销)。

您只需选择自己的方式并遵循它。


2
这不只是一种意见,对吗?至少从1980年代开始,软件工程研究就没有记录实时时间与错误数量之间的关系吗?
Mike Sherrill'Cat Recall'

1

您的两个示例在功能上是不同的代码,它们不可互换。(您的精简示例使它的区别没有区别,但是在非平凡的代码中确实有区别)。您选择的规则始终遵循作用域注意事项,如“ ...尽可能”所示。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.