我有一个小的依赖注入框架,我试图让它动态地解析Lazy<>
个实例。我的想法是做那样的事情:
DIContainer.Register<IDbCommand,SqlCommand>();
var lazyCommand = DIContainer.Resolve<Lazy<IDbCommand>>();
前几天我读到Autofac能够做到这一点。
我很难设法为Lazy<>
实例设置构造函数。在下一个测试代码中,抛出异常,因为所需的类型构造函数期望Func<arg>
,但我传递的是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);
}
我不知道如何创建适合Lazy<>
构造函数参数的委托。有什么想法吗?
干杯。
答案 0 :(得分:3)
这不是微不足道的。一种可能的解决方案是使用反射:
创建通用ResolveType
方法:
public static T ResolveType<T>()
{
return (T)ResolveType(typeof(T));
}
创建使用此方法的委托:
// 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);
使用该委托:
return Activator.CreateInstance(_lazyType.MakeGenericType(arg), delegate);
答案 1 :(得分:2)
此应用程序输出“True”和“0”。即ResolveTest(typeof(Lazy<int>))
会返回一个Lazy<int>
对象,其构造方式与您想要的一样。
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();
}
}
}
这是一种将ResolveTest
重写为通用Resolve<T>
的方式(例如Resolve<int>
返回Lazy<int>
)。这有点不同,因为没有等同于ResolveTest(typeof(int))
,它返回int
。
static Lazy<T> Resolve<T>()
{
var arg = typeof(T);
return new Lazy<T>(() => (T)ResolveType(arg));
}
或使用通用ResolveType<T>
:
static Lazy<T> Resolve<T>()
{
return new Lazy<T>(() => ResolveType<T>());
}
public static T ResolveType<T>()
{
return Activator.CreateInstance<T>();
}
答案 2 :(得分:1)
public static Object ResolveTest(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == _lazyType)
{
var arg = type.GetGenericArguments()[0];
Expression<Func<object>> expressionWithFuncOfTypeObject = () => ResolveType(arg);
UnaryExpression expressionThatEvaluatesToAnObjectOfTypeArg = Expression.Convert(expressionWithFuncOfTypeObject.Body, arg);
LambdaExpression expressionWithFuncOfTypeArg = Expression.Lambda(typeof(Func<>).MakeGenericType(arg), expressionThatEvaluatesToAnObjectOfTypeArg);
Delegate funcOfTypeArg = expressionWithFuncOfTypeArg.Compile(); // <-- At runtime this will be of type Func<T>
return Activator.CreateInstance(_lazyType.MakeGenericType(arg), funcOfTypeArg);
}
else
return ResolveType(type);
}