25

Resharper 建议这些变量:

List<string> senderDeviceIDList;
string senderDeviceID;
. . .
            foreach (var item in PlatypiIds)
            {
                senderDeviceIDList = await GetSenderDeviceIDForSenderID(item);
                senderDeviceID = senderDeviceIDList[0];

...可以在内部范围内声明,如下所示:

    foreach (var item in PlatypiIds)
    {
        List<string> senderDeviceIDList = await GetSenderDeviceIDForSenderID(item);
        string senderDeviceID = senderDeviceIDList[0];

...但这真的“更好”吗?这不会导致变量被声明 N 次(每个 foreach 循环一次)?

4

4 回答 4

33

顺便说一下,在性能或内存分配方面没有任何好处,因为范围或范围外的变量是在 , 中声明的。ifIL

唯一的好处是变量范围的本地化。将其移至使用它的范围,这会带来如下好处:

  • 易于重构(可能是最重要的)

  • 可读性。如果您在范围内看到变量,您就知道它在该范围内使用,如果您看到一些显然不在范围内的变量,您就知道更改它会影响代码的其他部分。所以改变它会带来一些潜在的危险。

简而言之,它与您正在编写的代码的可读性和可用性有关,并且不会带来任何性能或内存消耗优势。

于 2012-12-17T21:50:26.167 回答
10

这不会导致变量被声明 N 次(每个 foreach 循环一个)?

从逻辑上讲,从概念的角度来看是的,这就是重点!从逻辑上讲,它们每个循环存在一次,并且在循环范围之外没有任何意义。

作为一个实现细节,不,它不会导致创建多个局部变量。该方法将只有一个变量,并且将在一般情况下(以及在允许的情况下)重复使用。有一些例外情况,例如当您使用匿名方法关闭变量时,它无法重用变量。

请注意,由于 C# 强制您在使用它们之前初始化所有局部变量,因此运行时甚至不负责在每次循环后清除它,编译器不会让您重新使用之前的垃圾(除非您显式初始化它在循环开始时设置为默认值)。

于 2012-12-17T21:50:54.673 回答
3

无论如何,您每次迭代都会分配一次这些对象的实例,初始方法中唯一不同的是您声明引用一次,而不是像第二个示例中那样每次迭代。

如果您需要在 foreach 循环结束时使用这些对象的最终状态(多毛),那么您可能需要使用第一种方法。

于 2012-12-17T22:00:27.437 回答
0

有时利益是存在的。

如果生成的数组很大,那么将其移动到内部范围——即减少范围和生命周期——可能会阻止它被转移到后面的垃圾收集世代,并在有明显延迟的情况下被垃圾收集。


决定解释我的旧答案 [在对赞成票感到惊讶之后] 并部分放弃我的话 (27.10.2021)

细节#0。
您的两个选项的可见性范围不同,因此寿命也不同。在“for”循环中声明的变量从声明开始直到}这个循环,即直到每次迭代结束。这意味着变量在每次迭代结束后甚至不存在,直到它到达下一次迭代的声明行!

并且在“for”循环之外声明的变量在方法体的整个持续时间内都存在(我在这里假设在您的第一个示例中它是在某个方法体的顶层声明的)。

好的,有什么影响?

变量可以表示指向对象的指针。物体只有在至少有人指向它们时才会存在。因为如果没有人指着他们,怎么会有人提到他们?反之亦然——如果有人指向一个对象,它可能会在将来的某个时间点引用它,所以它必须存在。这正是垃圾收集器 (GC) 的逻辑。

因此,当您离开指向某个对象的变量的可见范围时,GC 可以停止考虑该指针。如果那个对象只有一个指向它自己的指针(你的变量),那么这个对象就已经不存在了,因为没有人指向它。但是,它继续存在于占用的内存方面 - 它是堆中的垃圾,等待 GC 被收集(在下一次垃圾收集运行或以后 - 由 GC 何时释放内存)。

回到您的选项 - 在方法执行过程中,当这些选项在是否允许 GC 或不收集您的变量指向的对象方面有所不同时,会出现一些问题。只要垃圾收集可能随时发生,将变量声明移动到内部范围只会增加该对象被收集的可能性。

Aaaaaand.. 99.9% 的时间你不应该担心它,因为这个对象无论如何都会在未来的某个时候被收集!但..

细节#1

不是,但”。真的 :)

不知道,我可能在 2016 年误以为 LOH(大对象堆)中的对象也有代,所以允许 GC 收集它们的速度有多快很重要——因为代越高,它的收集就越少。因此,第 2 代对象是非常长寿命的对象。现在不是这样(请参阅文档)- LOH 对象在其整个生命周期内都是第 2 代对象。所以,不要急于为 LOH 对象添加变量 - 你已经迟到了 :)

即使这些对象很大,但对于 LOH 来说还不够大,您可能仍然不必担心,直到找到必须调查此问题的证据(内存占用和性能)。

道德
所以,我的答案应该是有时利益存在[但可以忽略不计]。

指定正确的范围首先是对您自己和您的队友的帮助,而不是花哨的表演技巧。

于 2016-02-19T11:41:40.763 回答