具有循环和类型的高效代码 C#

发布于 2024-11-04 17:46:14 字数 212 浏览 3 评论 0原文


我想找出以下代码中哪些更有效(如果有的话)
值类型

ForEach(string s in strings)
{
  string t = s;
}
// or
string t;
ForEach(string s in strings)
{
   t = s;
}

与引用类型是否有不同。

I wanted to find out what more efficient (if even) in the following code
Value type

ForEach(string s in strings)
{
  string t = s;
}
// or
string t;
ForEach(string s in strings)
{
   t = s;
}

and is there a different with reference types.

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

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

发布评论

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

评论(8

迷鸟归林 2024-11-11 17:46:14

这两个代码片段生成完全相同相同的IL。下面是我用来测试的 C# 代码:

  public string[] strings = new string[5];

  public void TestOne()
  {
     foreach (string s in strings)
     {
        string t = s;
     }
  }

  public void TestTwo()
  {
     string t;
     foreach (string s in strings)
     {
        t = s;
     }
  }

下面是启用优化后编译后两种方法生成的 IL:

.method public hidebysig instance void  TestOne() cil managed
{
  // Code size       26 (0x1a)
  .maxstack  2
  .locals init ([0] string s,
           [1] string[] CS$6$0000,
           [2] int32 CS$7$0001)
  IL_0000:  ldarg.0
  IL_0001:  ldfld      string[] strings
  IL_0006:  stloc.1
  IL_0007:  ldc.i4.0
  IL_0008:  stloc.2
  IL_0009:  br.s       IL_0013
  IL_000b:  ldloc.1
  IL_000c:  ldloc.2
  IL_000d:  ldelem.ref
  IL_000e:  stloc.0
  IL_000f:  ldloc.2
  IL_0010:  ldc.i4.1
  IL_0011:  add
  IL_0012:  stloc.2
  IL_0013:  ldloc.2
  IL_0014:  ldloc.1
  IL_0015:  ldlen
  IL_0016:  conv.i4
  IL_0017:  blt.s      IL_000b
  IL_0019:  ret
} // end of method TestOne


.method public hidebysig instance void  TestTwo() cil managed
{
  // Code size       26 (0x1a)
  .maxstack  2
  .locals init ([0] string s,
           [1] string[] CS$6$0000,
           [2] int32 CS$7$0001)
  IL_0000:  ldarg.0
  IL_0001:  ldfld      string[] strings
  IL_0006:  stloc.1
  IL_0007:  ldc.i4.0
  IL_0008:  stloc.2
  IL_0009:  br.s       IL_0013
  IL_000b:  ldloc.1
  IL_000c:  ldloc.2
  IL_000d:  ldelem.ref
  IL_000e:  stloc.0
  IL_000f:  ldloc.2
  IL_0010:  ldc.i4.1
  IL_0011:  add
  IL_0012:  stloc.2
  IL_0013:  ldloc.2
  IL_0014:  ldloc.1
  IL_0015:  ldlen
  IL_0016:  conv.i4
  IL_0017:  blt.s      IL_000b
  IL_0019:  ret
} // end of method TestTwo

与往常一样,规则是相同的:信任您的编译器。让它为您处理这些优化,而不是在您编写代码时尝试担心它。只需编写有意义且可读的代码即可。

最重要的是,忽略所有认为其中一个“理论上”比另一个更好的人。这纯粹是无稽之谈。在代码编译之前,没有任何相关的理论。由于它编译为完全相同的东西,因此可以保证性能完全相同。

但是,如果您仍然不相信我的建议,相信您的编译器(我们中的一些人很顽固,我知道,因为即使我有时仍然如此),至少您现在知道如何自己回答这些类型的问题。编写一些演示这两种变体的示例代码,在“发布”模式下编译它,然后在 ILDASM.exe 并亲自比较生成的 IL 代码。像这样的事情很容易测试,没有理由去猜测。

The two code snippets produce exactly the same IL. Here's the C# code I used to test with:

  public string[] strings = new string[5];

  public void TestOne()
  {
     foreach (string s in strings)
     {
        string t = s;
     }
  }

  public void TestTwo()
  {
     string t;
     foreach (string s in strings)
     {
        t = s;
     }
  }

And here's the resulting IL for both methods after compiling with optimizations enabled:

.method public hidebysig instance void  TestOne() cil managed
{
  // Code size       26 (0x1a)
  .maxstack  2
  .locals init ([0] string s,
           [1] string[] CS$6$0000,
           [2] int32 CS$7$0001)
  IL_0000:  ldarg.0
  IL_0001:  ldfld      string[] strings
  IL_0006:  stloc.1
  IL_0007:  ldc.i4.0
  IL_0008:  stloc.2
  IL_0009:  br.s       IL_0013
  IL_000b:  ldloc.1
  IL_000c:  ldloc.2
  IL_000d:  ldelem.ref
  IL_000e:  stloc.0
  IL_000f:  ldloc.2
  IL_0010:  ldc.i4.1
  IL_0011:  add
  IL_0012:  stloc.2
  IL_0013:  ldloc.2
  IL_0014:  ldloc.1
  IL_0015:  ldlen
  IL_0016:  conv.i4
  IL_0017:  blt.s      IL_000b
  IL_0019:  ret
} // end of method TestOne


.method public hidebysig instance void  TestTwo() cil managed
{
  // Code size       26 (0x1a)
  .maxstack  2
  .locals init ([0] string s,
           [1] string[] CS$6$0000,
           [2] int32 CS$7$0001)
  IL_0000:  ldarg.0
  IL_0001:  ldfld      string[] strings
  IL_0006:  stloc.1
  IL_0007:  ldc.i4.0
  IL_0008:  stloc.2
  IL_0009:  br.s       IL_0013
  IL_000b:  ldloc.1
  IL_000c:  ldloc.2
  IL_000d:  ldelem.ref
  IL_000e:  stloc.0
  IL_000f:  ldloc.2
  IL_0010:  ldc.i4.1
  IL_0011:  add
  IL_0012:  stloc.2
  IL_0013:  ldloc.2
  IL_0014:  ldloc.1
  IL_0015:  ldlen
  IL_0016:  conv.i4
  IL_0017:  blt.s      IL_000b
  IL_0019:  ret
} // end of method TestTwo

As usual, the rule is the same: trust your compiler. Let it handle these optimizations for you, rather than trying to worry about it as you write the code. Just write the code that makes sense and is readable.

Most importantly, ignore all the people who argue that one of them is "theoretically" better than the other. That's pure nonsense. There's no theory that's relevant until after the code is compiled. And since it compiles down to the exact same thing, it's guaranteed to be completely identical in performance.

But if you still don't believe my urgings to trust your compiler (some of us are stubborn, I know because even I still am sometimes), at least you now know how to answer these types of questions on your own. Write some sample code that demonstrates both variations, compile it in "Release" mode, then open up the assembly in ILDASM.exe and compare the resulting IL code for yourself. Things like this are so easy to test, there's never a reason to guess.

转身泪倾城 2024-11-11 17:46:14

好吧,这些都不会编译(我猜你想使用 foreach 而不是 ForEach?),但如果他们这样做了,他们应该在优化后编译到相同的 IL所以不会有什么区别。

Well neither of those will compile (I guess that you want to use foreach instead of ForEach?), but if they did they should compile down to the same IL after optimizations and so there would be no difference.

娇纵 2024-11-11 17:46:14

你根本不需要这样做。只需使用s即可。

另外,C#/BCL 中的string 是一种引用类型。

You don't need to do this at all. Just use s.

Also, string in C#/BCL is a reference type.

梦年海沫深 2024-11-11 17:46:14

它们的内存效率是相同的,因为它们在幕后都指向同一个字符串。这是 .NET/Java 使用的语义,也是字符串不可变的原因。

They're both the same for memory effiency, because behind the scenes they all point to the same string. This is a semantic used by .NET/Java, and the reason why strings are immutable.

金兰素衣 2024-11-11 17:46:14

从理论上讲,第二个应该更有效,因为内存分配仅在循环开始之前执行一次,而第一个应该在每次迭代时尝试分配内存。

但是我不知道编译器是否根据变量在循环内部和外部的使用方式执行任何优化(在第二种情况下,变量可以在代码的其他部分使用,而不仅仅是在循环中)。

Theoretically speaking, the second one should be more efficient because the memory allocation is performed only once before the loop starts, while the first one should try to allocate memory at every iteration.

However i do not know if there is any optimization performed by the compiler based on how the variable is used inside and outside the loop (in the second case the variable could be used in other parts of the code, not only in the loop).

浪漫人生路 2024-11-11 17:46:14

字符串是不可变的引用类型

因此,代码

string t;
foreach(string s in strings)
{
   t = s;
}

无论如何都会为枚举器中的每个步骤创建一个新的字符串实例(它不会“重用”实例t)。

您发布的两个代码示例之间没有实际区别。

strings are immutable reference types.

So the code

string t;
foreach(string s in strings)
{
   t = s;
}

creates a new string instance for each step in the enumerator anyway (it doesn't "reuse" the instance t).

There is no practical difference between the two code examples you posted.

若水般的淡然安静女子 2024-11-11 17:46:14

我敢打赌编译器足够智能,可以以完全相同的方式构建它们。我认为没有什么区别,继续阅读。

I would bet that the compiler is intelligent enough to build them in exactly the same way. I don't think there is any difference, go on readability.

忆伤 2024-11-11 17:46:14

这是相同的代码,只是声明在另一个地方。您应该始终尝试使用尽可能小的作用域在变量的使用旁边声明变量。

请注意,foreach 与 C# 中的所有关键字一样都是小写的。

This is the same code, only the declaration is at another place. You should always try to declare the variables next to their use, using the smallest possible scope.

Note that foreach is written lowercase like all keywords in c#.

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