[Intermediate level]
[This blog post was originally published on an internal company blog on February 20th 2011]
Hi All,
Couple days back I was writing code as usual and I decided to use a lambda expression as a function. The code I wrote looked something like this:
public void Moo(){int x = 10;SomeEvent += (i) => { Console.Out.WriteLine(x); }; }
Then, I stopped for a second. What is going on here? I just used a local variable in a method that is going to be called long after the stack frame where this local variable was defined is gone. This doesn't make any sense. Clearly, once the program counter (instruction pointer) leaves the method Moo, the variable x defined on the stack will be freed. I ran the code and everything worked perfectly, 10 was printed on the screen once SomeEvent was fired. Strange!
I decided to ask a friend... "What is going on here?" I asked. "Maybe it's like anonymous class" he said. This actually made some sense to I decided to dig deeper using my trusty (but no longer free) friend: Reflector.
Before I begin copy-pasting IL code, a word about anonymous classes. In .NET 3.5 Microsoft introduced LINQ which allows easy queries on various types of collections. But they noticed a problem. You can have many different types of queries on the same collection and every time a result record would look different. For example if you have a DB table with the colums ID, Name and Title. Sometimes you want to retrieve only the name, sometimes only the title, sometimes you want to count the number of rows for each name (so the result is (Count, Name)). Previously the user would have to define a new class or struct for each return type but anonymous classes solve this by using the "var" keyword.
(Yes, this is the correct place to use "var" and not when you are too lazy to write the actual variable type!)
I will not give a concrete example on how to use LINQ but for example if I want to define a new class that has two fields, Count and Name I can do it like:
var p = new { Name = "Boris", Count = 10 };
Now I can access p.Count or p.Name as usual; Anyway... I wrote a small class to see what IL created. The content of the file is:
using System;using System.Collections.Generic; using System.Linq;using System.Text;namespace ConsoleApplication3{public delegate void HowItWorksHandler(int number); class Class1{public event HowItWorksHandler SomeEvent; public Class1(){}public void Moo(){int x = 10;SomeEvent += (i) => { Console.Out.WriteLine(x); }; }public void Honey(){var p = new { Name = "Boris", Count = 10 }; }public void Sheep(){SomeEvent += (i) => { Console.Out.WriteLine(i + 1); }; }public void Memory(){MemoryHolder mh = new MemoryHolder(); SomeEvent += (x) => {mh.BigMemory = 10;}; }void Class1_How(intnumber) {throw new NotImplementedException(); }}public class MemoryHolder{public int BigMemory;public MemoryHolder(){Console.Out.WriteLine("Memory holder created"); }~MemoryHolder(){Console.Out.WriteLine("Memory holder free"); }}}Now lets start from the end... For the method Honey where I used an anonymous class a new class was created as expected. It was located in the dll but with no namespace and had quite a full definition:
[CompilerGenerated, DebuggerDisplay(@"\{ Name = {Name}, Count = {Count} }", Type="<Anonymous Type>")] internal sealed class <>f__AnonymousType0<<Name>j__ |
Nothing of much interest here but note that the compiler overrides the default methods to some less generic implementation. But, the really interesting method is the method Moo. Let's look at the IL there:
|