Do you grok C# 2.0: Answers
So a couple of days ago I posted the Do you grok C# 2.0 challange. Here is the code that I presented:
using System.Collections.Generic;
public class Sample<T> where T:class
{
private static string typeName = typeof(T).FullName;
public string TypeName
{
get { return typeName; }
}
public List<Printer> PrintNumbers()
{
string [] names = { "Ayende", "Rahien", "Foo", "Bar" };
List<Printer> list = new List<Printer> ();
foreach(string s in names)
{
list.Add( delegate(){ Console.WriteLine(s); } );
}
return list;
}
}
public delegate void Printer();
public class Start
{
public static void Main()
{
Console.WriteLine(new Sample<Type>().TypeName);
Console.WriteLine(new Sample<string>().TypeName);
foreach(Printer print in new Sample<Start>().PrintNumbers())
{
print();
}
}
}
The output of this code is:
System.String
Bar
Bar
Bar
Bar
What is going on in here? Well, the two first lines are different because the compiler generate a different class for each generic type that you pass. This means that the static field is different, so it stores two different values, even though it's look like we only have one.
This is useful because you can use a static field to store information that is only relevant for one type of the class. I'm taking advantage of this to cache information on the type level.
The second issue is the printing of Bar, Bar, Bar, Bar. Beer and bars aside, the problem here is that the C# compiler doesn't produce a true closure in this case. It sees that we're only using a single variable, so it promote it to a field, and all the anonymous delegates we've created will use the class field. So in this case, they will all point to the same field, and all print the same item.
A way around this is to introduce a temporary variable, which would force the compiler to make sure that each delegate has its own values, and so it would create a class for the whole thing, and we would get the expected output, with each delegate printing a different value.
Comments
Comment preview