【问题标题】:How to create a Func<> delegate programmatically如何以编程方式创建 Func<> 委托
【发布时间】:2013-10-24 01:40:03
【问题描述】:

我有一个小的依赖注入框架,我试图让它动态解析Lazy&lt;&gt; 实例。我们的想法是做这样的事情:

DIContainer.Register<IDbCommand,SqlCommand>();

var lazyCommand = DIContainer.Resolve<Lazy<IDbCommand>>();

前几天我读到 Autofac 能够做到这一点。

我一直在尝试为 Lazy&lt;&gt; 实例设置构造函数。在下一个测试代码中,由于所需的类型构造函数期望 Func&lt;arg&gt;,而引发异常,但我传递的是 Func&lt;Object&gt;:

    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&lt;&gt; 构造函数参数的委托。有什么想法吗?

干杯。

【问题讨论】:

标签: c# reflection func


【解决方案1】:

这不是微不足道的。一种可能的解决方案是使用反射:

  1. 创建一个通用的ResolveType 方法:

    public static T ResolveType<T>()
    {
        return (T)ResolveType(typeof(T));
    }
    
  2. 创建一个使用此方法的委托:

    // 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);
    
  3. 使用该委托:

    return Activator.CreateInstance(_lazyType.MakeGenericType(arg), delegate);
    

【讨论】:

  • 你忘了method.MakeGenericMethod(arg)。
  • +1 为你丹尼尔提供了这个很好的答案。 vtortola 检查这个非常好@JonSkeet stackoverflow.com/questions/773099/…
  • 它不起作用:“无法绑定到目标方法,因为它的签名或安全透明度与委托类型的不兼容。”
  • @BassamAlugili 它不仅是创建函数,而且是调用时必须在其中运行的逻辑。
  • @EliArbel:确实如此。忘记从测试项目中复制它。固定
【解决方案2】:

此应用输出“True”和“0”。 IE。 ResolveTest(typeof(Lazy&lt;int&gt;)) 返回一个 Lazy&lt;int&gt; 对象,按照您的意愿构建。

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&lt;T&gt; 的方法(例如Resolve&lt;int&gt; 返回Lazy&lt;int&gt;)。这有点不同,因为没有等价于 ResolveTest(typeof(int)),它返回一个 int。

static Lazy<T> Resolve<T>()
{
    var arg = typeof(T);
    return new Lazy<T>(() => (T)ResolveType(arg));
}

或者使用通用的ResolveType&lt;T&gt;:

static Lazy<T> Resolve<T>()
{
    return new Lazy<T>(() => ResolveType<T>());
}
public static T ResolveType<T>()
{
    return Activator.CreateInstance<T>();
}

【讨论】:

  • 我觉得不用表达,但很有趣。谢谢!
  • @vtortola 我找不到更好的方法将object ResolveType(Type) 的返回值转换为类型。
【解决方案3】:
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);
}

【讨论】:

  • 我喜欢你不需要静态辅助方法:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-27
  • 2020-07-01
  • 2011-09-29
  • 2013-12-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多