C# lambda and foreach variable
Posted by Bojan Resnik on June 17, 2009
Can you guess what the output of the following program is?
using System; using System.Collections.Generic; namespace Lambda { class Program { static void Main(string[] args) { var strings = new[] {"a", "b", "c"}; var actions = CreateActions(strings); actions.ForEach(f => f()); } private static List<Action> CreateActions(IEnumerable<string> strings) { var actions = new List<Action>(); foreach (var s in strings) actions.Add( () => Console.WriteLine(s) ); return actions; } } }
I expected to see all three strings (a, b and c), but instead I got this:
While analyzing the IL code with Reflector I learned a thing or two about lambdas in C#.
When a lambda is encountered by the compiler, it generates a class which has a field for each local variable used by the lambda. In this case, the generated class would look like this in C#:
private sealed class <>c_DisplayClass3 { public string s; public void <Main>b_0() { Console.WriteLine(this.s); } }
Of course, the above is not valid C# due to invalid characters in identifiers, but is pretty much what is produced in the IL.
So far so good – no real surprises here. However, look at this code which is my translation from IL to C# of the CreateActions method:
private static List<Action> CreateActions(IEnumerable<string> strings) { List<Action> actions = new List<Action>(); using (IEnumerator<string> enumerator = strings.GetEnumerator()) { // Note 1: The lambda is created outside of the loop <>c_DisplayClass3 lambda = new <>c_DisplayClass3(); string s; while (enumerator.MoveNext()) { s = enumerator.Current; // Note 2: The instance field is reassigned each time lambda.s = s; actions.Add(lambda); } } return actions; }
The reason for the strange output is clear now – the compiler did not create a lambda object in each iteration but rather reused the existing one each time. After the loop, all items in the actions list are actually the same instance and their s field is the last element of the collection. Apparently, C# compiler creates a lambda instance immediately before the local variable it uses. So, in order to force it to create a new lambda for each iteration, we need to introduce a new local variable in the loop:
private static List<Action> CreateActions(IEnumerable<string> strings) { var actions = new List<Action>(); foreach (var s in strings) { // This variable will be used in the lambda var lambda_param = s; actions.Add( () => Console.WriteLine(lambda_param) ); } return actions; }
With this modification the program now produces this output:
Flash said
Great post!
Tormod said
Nice and precise. Informative. Not only regarding Lambdas, but also how the transparency offered by the occasional C# to IL to C# dive is an essential skill that at least should be somewhere in the development team.
Tormod said
After looking a bit more, I realize that the loop returns exactly what is expected. A lambda performed on an “s” reference, which changes before the lambda is actually executed, should not behave any differently.
DotNetBurner - C# said
C# lambda and foreach variable…
DotNetBurner – burning hot .net content…
Jason Young said
That is really odd. Can anyone explain WHY it works this way? That doesn’t seem intuitive or easily recognizable.
Jeff Becker said
Because a closure captures the variable, not the value.
Bojan Resnik said
Before exploring this, I expected the compiler to create a new lambda object each time it encouters a lambda definition. However, I guess that for the typical usage of lambdas this would be just a waste of time and space.
CodeThinked | Reflecting Code Is Not A Perfect Science said
[…] All this time though, I knew that there would be some very interesting translations and manipulations that occurred, but I didn’t really consider that the code which reflector spit out might not actually execute in the same way as the code that I was compiling. Well, today I found just that case (in the latest version of Reflector) while checking out this blog post. […]
Weekly Link Post 107 « Rhonda Tipton’s WebLog said
[…] C# lambda and foreach variable […]
Thorn said
As usual, M$ “tricks” leads to ugly code style, named ‘Microsoft experience’.
Who cares your damn ‘effectiveness’ when you produce really wrong code? As M$ promises, “JIT” must eliminate all bottlenecks, giving optimized, native code. So why you make your own optimizations?
Using foreach with PredicateBuilder « 64 Square Feet said
[…] C# Lambda and foreach variable Posted by Dana Filed in Uncategorized Tags: c#, lambda, Microsoft MVC, PredicateBuilder Leave a Comment » […]
MKPFS said
Yeah, that’s why in Java anonymous methods can only access local variables if they are final.
BTW putting lambdas into procedural/OOP languages makes me sick.
Ecko said
thanks, great post
Ismail Karolia said
Wow. Great post.
I experienced the exact same problem and couldn’t figure out what the problem was.
Thanks alot.
James Jensen said
FYI: Eric Lippert says this behavior will change in C# 5: http://stackoverflow.com/questions/8898925/is-there-a-reason-for-cs-reuse-of-the-variable-in-a-foreach/8899347#8899347
Bojan Resnik said
Great! Thanks for the info.
Cheers,
Bojan
nicdex said
the javascript closure works the same way. you need to declare a local var to have the right value. maybe it’s why they implemented it this way in c#.
chambem9 said
The reason that it was originally implemented that way is because in C#1 there was no concept of closures, so it made no difference whether the variable was declared inside or outside the loop. (I guess they decided to use the outer scope for efficiency.) When closures were implemented, this behaviour was then inherited for reasons of backwards compatibility. As Eric Lippert points out on his blog (http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx), this was something they always wanted to change, and he feels it’s a shame that it’s taken so long to do so.
Dev said
interesting post..’yield’ would directly give the desired result.
IEnumerable CreateActions(string[] str)
{
foreach (var item in str)
{
yield return () => Console.WriteLine(item);
}
}
Is there a reason for C#’s reuse of the variable in a foreach? | Everyday I'm coding said
[…] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]
quantrim usage said
Spot on with this write-up, I truly believe that this web site needs much more attention.
I’ll probably be back again to see more, thanks for the information!
Is there a reason for C#s reuse of the variable in a foreach? - Tech Forum Network said
[…] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]
Is there a reason for C#’s reuse of the variable in a foreach? | Everyday I'm coding said
[…] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]
chamo1 said
what if the collection is a reference type? How do you go about solving it? Moreover, what if you want to update a property of the reference type item? like so:
var dbEmployees = GetExistingEmployees();
IList employees = new List { new Employee{ Id = 1, EmpType = new EmployeeType { id = 1},…}, new Employee{ Id = 2, EmpType = new EmployeeType { id = 1},…}}
foreach (var emp in employees)
{
foreach (var oldEmp in dbEmployees)
{
if (emp.Id == oldEmp.Id)
{
UpdateChanges(r, oldEmp);
existingEmp.Add(r);
}
}
}
private void UpdateChanges(Employee emp, Employee oldEmp){
if (oldEmp.EmpType.Id != emp.EmpType.Id)
{
LogChange();
oldEmp.EmpType.Id != emp.EmpType.Id;
}
}
The issue here is the last employee in the collection if his/her employee type property Id changed in a ddl, updating it will cascade to all other employees’ emp type in the collection. That is nutts. Due to the logging requirement I can not use lambda or other fancy construct. I need hep with fixing this with in foreach or for loops.
Bojan Resnik said
I’m not sure I understand the problem – ultimately, it has nothing to do with lambda or foreach. You might want to ask a question on StackOverflow instead.
Cheers,
Bojan
Is there a reason for C#'s reuse of the variable in a foreach? - oneuptime | oneuptime said
[…] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]
Is there a reason for C#’s reuse of the variable in a foreach? | Ask Programming & Technology said
[…] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]
How to: Is there a reason for C#'s reuse of the variable in a foreach? | SevenNet said
[…] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]
How to: Is there a reason for C#'s reuse of the variable in a foreach? | Technical information for you said
[…] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]
Fixed Is there a reason for C#'s reuse of the variable in a foreach? #dev #it #asnwer | Good Answer said
[…] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]
Is there a reason for C#'s reuse of the variable in a foreach? - Technology said
[…] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]
Is there a reason for C#'s reuse of the variable in a foreach? - TecHub said
[…] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]
Community Convergence LIV | Charlie Calvert's Community Blog said
[…] C# lambda and foreach variable […]
Is there a reason for C#'s reuse of the variable in a foreach? - ExceptionsHub said
[…] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]
Is there a reason for C#’s reuse of the variable in a foreach? – Knowleage Exchange said
[…] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]
Есть ли причина повторного использования С# переменной в foreach? — Вопросы и ответы по программированию said
[…] объясняется здесь, это происходит […]
Reflecting Code Is Not A Perfect Science - Simple Thread said
[…] All this time though, I knew that there would be some very interesting translations and manipulations that occurred, but I didn’t really consider that the code which reflector spit out might not actually execute in the same way as the code that I was compiling. Well, today I found just that case (in the latest version of Reflector) while checking out this blog post. […]
foreach에서 C #이 변수를 재사용하는 이유가 있습니까? 또는 익명 메서드를 사용하는 경우 - How IT said
[…] here 설명 된 것처럼 위의 루프 s에서 선언 된 변수 foreach가 컴파일러에서 다음과 같이 변환 되기 때문에 발생 합니다. […]
Is there a reason for C#'s reuse of the variable in a foreach? - PhotoLens said
[…] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]
Apakah ada alasan untuk menggunakan kembali variabel C # di foreach? - Dev TataWeb said
[…] yang dijelaskan di sinihal ini terjadi karena s variabel dideklarasikan dalam foreach loop di atas diterjemahkan seperti […]
Apakah ada alasan untuk penggunaan ulang C # dari variabel dalam foreach? – Kode Contoh said
[…] yang dijelaskan di sini , ini terjadi karena svariabel yang dideklarasikan dalam foreachloop di atas diterjemahkan seperti […]