Let's suppose we have the following program:
public class Program
{
private static Dictionary<Type, Func<object, object>> converters = new Dictionary<Type, Func<object[], object>>();
public static void Main(string[] args)
{
RegisterImplementation(new IntConverter());
int value = (int) dic[typeof(int)]("4");
Console.WriteLine(value); //Prints 4
}
private static RegisterImplementation<X>(IConverter<X> converter)
{
Type type = typeof(X);
Func<object, object> conversion = (obj) => converter.Convert(obj);
if(dic.ContainsKey(type))
dic[type] = conversion;
else
dic.Add(type, conversion);
}
}
public interface IConverter<X>
{
X Convert(object obj);
}
public class IntConverter : IConverter<int>
{
public int Convert(object obj)
{
return Convert.ToInt32(obj);
}
}
I understand most of the code, but the part that's driving me mad is the RegisterImplementation
method. In the dictionary we are storing a Func<object, object>
instance, and the converter
is not stored anywhere, so I am assuming we lose the local reference when we get out of the method.
So how can we call the function in the dictionary afterwards and use this reference of IntConverter
? Where is it stored? Inside the Func<object, object>
?
Firstly, it's worth being clear that your question doesn't actually involve expression trees at all - your lambda expression is just being converted into a delegate.
Now, that lambda expression is this:
(obj) => converter.Convert(obj)
That captures the local variable, converter
. In practice, that means the C# compiler will have created a new class, like this:
private class UnspeakableName<X>
{
public IConverter<X> converter;
public object Method(object obj)
{
return converter(obj);
}
}
Then your method will be converted into:
private static RegisterImplementation<X>(IConverter<X> converter)
{
UnspeakableName<X> tmp = new UnspeakableName<X>();
tmp.converter = converter;
Type type = typeof(X);
Func<object, object> conversion = tmp.Method;
if(dic.ContainsKey(type))
dic[type] = conversion;
else
dic.Add(type, conversion);
}
So the target of the delegate will be the instance of the new class, and that keeps the converter alive.
See more on this question at Stackoverflow