简体   繁体   中英

Efficient code with loops and types C#


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.

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.

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.

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

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

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.

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).

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.

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.

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#.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM