I have a small dependency injection framework, and I am trying to make it resolve Lazy<> instances dynamically. The idea is to do something like that:
DIContainer.Register<IDbCommand,SqlCommand>();
var lazyCommand = DIContainer.Resolve<Lazy<IDbCommand>>();
I read the other day that Autofac was able of doing that.
I am stuck trying to set the constructor for that Lazy<> instance. In the next test code, a exception is thrown because the desired type constructor is expecting a Func<arg>, but I am passing a Func<Object>:
    static readonly Type _lazyType = typeof(Lazy<>);
    static Object ResolveTest(Type type)
    {
        if (type.IsGenericType && type.GetGenericTypeDefinition() == _lazyType)
        {
            var arg = type.GetGenericArguments()[0];
            return Activator.CreateInstance(_lazyType.MakeGenericType(arg), new Func<Object>(() => ResolveType(arg)));
        }
        else 
            return ResolveType(type);
    }
I am out of ideas about how to create a delegate that fits for the Lazy<> constructor parameter. Any idea?
Cheers.
That's not trivial. One possible solution would be to work with reflection:
Create a generic ResolveType method:
public static T ResolveType<T>()
{
    return (T)ResolveType(typeof(T));
}
Create a delegate that uses this method:
// You probably want to cache this MethodInfo:
var method = typeof(TypeContainingResolveType)
                 .GetMethods()
                 .Single(x => x.IsGenericMethod && 
                              x.Name == "ResolveType")
                 .MakeGenericMethod(arg);
var delegate = Delegate.CreateDelegate(
                   typeof(Func<>).MakeGenericType(arg),
                   method);
Use that delegate:
return Activator.CreateInstance(_lazyType.MakeGenericType(arg), delegate);
This app outputs "True" and "0". I.e. ResolveTest(typeof(Lazy<int>)) returns a Lazy<int> object, constructed like you wanted.
using System;
using System.Linq.Expressions;
namespace TestApp
{
    public class Class1
    {
        public static void Main()
        {
            object lazyInt = ResolveTest(typeof(Lazy<int>));
            Console.WriteLine(lazyInt.GetType() == typeof(Lazy<int>));
            Console.WriteLine(((Lazy<int>)lazyInt).Value);
        }
        static readonly Type _lazyType = typeof(Lazy<>);
        static Object ResolveTest(Type type)
        {
            if (type.IsGenericType && type.GetGenericTypeDefinition() == _lazyType)
            {
                var arg = type.GetGenericArguments()[0];
                var lazyArgType = _lazyType.MakeGenericType(arg);
                var funcArgType = typeof(Func<>).MakeGenericType(arg);
                var funcCtor = lazyArgType.GetConstructor(new[] { funcArgType });
                Expression<Func<object>> f = () => ResolveTest(arg);
                var func = typeof(Class1).GetMethod("BuildCastedThing").MakeGenericMethod(arg).Invoke(null, new[] { f });
                var arguments = new object[] { func };
                var retVal = funcCtor.Invoke(arguments);
                return retVal;
            }
            else
                return ResolveType(type);
        }
        public static object ResolveType(Type type)
        {
            return Activator.CreateInstance(type);
        }
        public static Func<T> BuildCastedThing<T>(Expression<Func<object>> f)
        {
            Expression<Func<T>> expr =
                Expression.Lambda<Func<T>>(
                    Expression.Convert(
                        Expression.Invoke(f),
                        typeof(T)));
            return expr.Compile();
        }
    }
}
This is a way to rewrite ResolveTest as a generic Resolve<T> (e.g. Resolve<int> returns Lazy<int>). This is a little different, since there's no equivalent to ResolveTest(typeof(int)), which returns an int.
static Lazy<T> Resolve<T>()
{
    var arg = typeof(T);
    return new Lazy<T>(() => (T)ResolveType(arg));
}
Or with a generic ResolveType<T>:
static Lazy<T> Resolve<T>()
{
    return new Lazy<T>(() => ResolveType<T>());
}
public static T ResolveType<T>()
{
    return Activator.CreateInstance<T>();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With