变量声明是否应该始终放在循环之外?

发布于 2024-09-09 00:31:13 字数 912 浏览 3 评论 0原文

在循环外部而不是内部声明循环中使用的变量是否更好?有时我会看到在循环内声明变量的示例。这是否会有效地导致程序在每次循环运行时为新变量分配内存?或者 .NET 是否足够聪明,知道它实际上是同一个变量。

例如,请参阅此答案<中的以下代码< /a>.

public static void CopyStream(Stream input, Stream output)
{
    byte[] buffer = new byte[32768];
    while (true)
    {
        int read = input.Read (buffer, 0, buffer.Length);
        if (read <= 0)
            return;
        output.Write (buffer, 0, read);
    }
}

这个修改后的版本会更有效吗?

public static void CopyStream(Stream input, Stream output)
{
    int read; //OUTSIDE LOOP
    byte[] buffer = new byte[32768];
    while (true)
    {
        read = input.Read (buffer, 0, buffer.Length);
        if (read <= 0)
            return;
        output.Write (buffer, 0, read);
    }
}

Is it better to declare a variable used in a loop outside of the loop rather then inside? Sometimes I see examples where a variable is declared inside the loop. Does this effectively cause the program to allocate memory for a new variable each time the loop runs? Or is .NET smart enough to know that it's really the same variable.

For example see the code below from this answer.

public static void CopyStream(Stream input, Stream output)
{
    byte[] buffer = new byte[32768];
    while (true)
    {
        int read = input.Read (buffer, 0, buffer.Length);
        if (read <= 0)
            return;
        output.Write (buffer, 0, read);
    }
}

Would this modified version be any more efficent?

public static void CopyStream(Stream input, Stream output)
{
    int read; //OUTSIDE LOOP
    byte[] buffer = new byte[32768];
    while (true)
    {
        read = input.Read (buffer, 0, buffer.Length);
        if (read <= 0)
            return;
        output.Write (buffer, 0, read);
    }
}

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

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

发布评论

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

评论(6

满地尘埃落定 2024-09-16 00:31:14

不,这不会更有效率。但是,我会以这种方式重写它,无论如何,它都会在循环之外声明它:

byte[] buffer = new byte[32768];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
    output.Write(buffer, 0, read);
}

我通常不喜欢在条件中使用副作用,但实际上 Read 方法为您提供了两个数据位:您是否已到达流的末尾,以及您已读取了多少数据。 while 循环现在说:“当我们设法读取一些数据时......复制它。”

这有点像使用 int.TryParse

if (int.TryParse(text, out value))
{
    // Use value
}

您再次使用了在条件中调用方法的副作用。正如我所说,当您处理返回两位数据的方法时,我不会养成这样做的习惯除非对于这种特定模式。

TextReader 读取行时也会出现同样的情况:

string line;
while ((line = reader.ReadLine()) != null)
{
    ...
}

回到原来的问题:如果要在循环的每次迭代中初始化变量并且,那么它只是在循环体内使用,我几乎总是在循环内声明它。这里的一个小例外是,如果变量被匿名函数捕获 - 此时它将在行为上产生差异,我会选择给我所需行为的任​​何形式......但这几乎总是“在内部声明” “无论如何形成。

编辑:当谈到作用域时,上面的代码确实将变量保留在比需要的更大的作用域中......但我相信它使循环更清晰。如果您愿意,您始终可以通过引入新范围来解决此问题:

{
    int read;
    while (...)
    {
    }
}

No, it wouldn't be more efficient. However, I'd rewrite it this way which happens to declare it outside the loop anyway:

byte[] buffer = new byte[32768];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
    output.Write(buffer, 0, read);
}

I'm not generally a fan of using side-effects in conditions, but effectively the Read method is giving you two bits of data: whether or not you've reached the end of the stream, and how much you've read. The while loop is now saying, "While we've managed to read some data... copy it."

It's a little bit like using int.TryParse:

if (int.TryParse(text, out value))
{
    // Use value
}

Again you're using a side-effect of calling the method in the condition. As I say, I don't make a habit out of doing this except for this particular pattern, when you're dealing with a method returning two bits of data.

The same thing comes up reading lines from a TextReader:

string line;
while ((line = reader.ReadLine()) != null)
{
    ...
}

To go back to your original question: if a variable is going to be initialized in every iteration of a loop and it's only used within the body of the loop, I'd almost always declare it within the loop. One minor exception here is if the variable is being captured by an anonymous function - at that point it will make a difference in behaviour, and I'd pick whichever form gave me the desired behaviour... but that's almost always the "declare inside" form anyway.

EDIT: When it comes to scoping, the code above does indeed leave the variable in a larger scope than it needs to be... but I believe it makes the loop clearer. You can always address this by introducing a new scope if you care to:

{
    int read;
    while (...)
    {
    }
}
你的心境我的脸 2024-09-16 00:31:14

在不太可能的环境中,这对您没有帮助,它仍然是一个微观优化。清晰度和适当的范围界定等因素比边缘情况重要得多,在边缘情况下这可能几乎没有什么区别。

您应该为变量提供适当的范围,而不考虑性能。当然,复杂的初始化是一个不同的野兽,因此如果某些东西只应初始化一次但仅在循环内使用,您仍然希望在外部声明它。

In the unlikely environment that doesn't help you with this, it would still be a micro-optimization. Factors like clarity and proper scoping is much more important than the edge case where this might just make next to no difference.

You should give your variables proper scope without thinking about performance. Of course, complex initializations are a different beast, so if something should only be initialized once but is only used within a loop, you'd still want to declare it outside.

初相遇 2024-09-16 00:31:14

我同意大多数其他答案,但需要注意。

如果您使用 lambda 表达式,则必须小心捕获变量。

static void Main(string[] args)
{
    var a = Enumerable.Range(1, 3);
    var b = a.GetEnumerator();
    int x;
    while(b.MoveNext())
    {
        x = b.Current;
        Task.Factory.StartNew(() => Console.WriteLine(x));
    }
    Console.ReadLine();
}

将给出结果

3
3
3

哪里

static void Main(string[] args)
{
    var a = Enumerable.Range(1, 3);
    var b = a.GetEnumerator();
    while(b.MoveNext())
    {
        int x = b.Current;
        Task.Factory.StartNew(() => Console.WriteLine(x));
    }
    Console.ReadLine();
}

将给出结果

1
2
3

或其中的某种顺序。这是因为当任务最终启动时,它将检查它对 x 的引用的当前值。在第一个示例中,所有 3 个循环都指向同一引用,而在第二个示例中,它们都指向不同的引用。

I am going to agree with most of these other answers with a caveat.

If you are using lambada expressions you must be careful with capturing variables.

static void Main(string[] args)
{
    var a = Enumerable.Range(1, 3);
    var b = a.GetEnumerator();
    int x;
    while(b.MoveNext())
    {
        x = b.Current;
        Task.Factory.StartNew(() => Console.WriteLine(x));
    }
    Console.ReadLine();
}

will give the result

3
3
3

Where

static void Main(string[] args)
{
    var a = Enumerable.Range(1, 3);
    var b = a.GetEnumerator();
    while(b.MoveNext())
    {
        int x = b.Current;
        Task.Factory.StartNew(() => Console.WriteLine(x));
    }
    Console.ReadLine();
}

will give the result

1
2
3

or some order there of. This is because when the task finally starts it will check the current value of it's reference to x. in the first example all 3 loops pointed at the same reference, where in the second example they all pointed at different references.

神魇的王 2024-09-16 00:31:14

与许多像这样的简单优化一样,编译器会为您处理它。如果您尝试这两种方法并在 ildasm 中查看程序集的 IL,您可以看到它们都声明了一个 int32 读取变量,尽管它确实对声明进行了重新排序:

  .locals init ([0] int32 read,
           [1] uint8[] buffer,
           [2] bool CS$4$0000)

  .locals init ([0] uint8[] buffer,
           [1] int32 read,
           [2] bool CS$4$0000)

As is the case with lots of simple optimizations like this, the compiler takes care of it for you. If you try both of these and look at the assemblies' IL in ildasm you can see that they both declare a single int32 read variable, although it does reorder the declarations:

  .locals init ([0] int32 read,
           [1] uint8[] buffer,
           [2] bool CS$4$0000)

  .locals init ([0] uint8[] buffer,
           [1] int32 read,
           [2] bool CS$4$0000)
溺ぐ爱和你が 2024-09-16 00:31:14

这真的不重要,如果我正在审查该特定示例的代码,我不会关心任何一种方式。

但是,请注意,如果您最终在闭包中捕获“read”变量,则这两者可能意味着非常不同的事情。

请参阅 Eric Lippert 的这篇精彩文章,其中出现了有关 foreach 循环的问题 - 链接

It really doesn't matter, and if I was reviewing the code for that particular example, I wouldn't care either way.

However, be aware that the two can mean very different things if you end up capturing the 'read' variable in a closure.

See this excellent post from Eric Lippert where this issue comes up regarding foreach loops - Link

我只土不豪 2024-09-16 00:31:14

作为个人习惯,我通常更喜欢后者,因为即使 .NET 足够智能,我以后工作的其他环境也可能不够智能。它可能只不过是在循环内编译成一行额外的代码来重新初始化变量,但这仍然是开销。

即使它们在任何给定示例中的所有可测量目的都是相同的,我想说,从长远来看,后者引起问题的可能性较小。

I've generally preferred the latter as a matter of personal habit because, even if .NET is smart enough, other environments in which I might work later may not be smart enough. It could be nothing more than compiling down to an extra line of code inside the loop to re-initialize the variable, but it's still overhead.

Even if they're identical for all measurable purposes in any given example, I would say the latter has less of a chance of causing problems in the long run.

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