如何从.NET 3.5中的函数返回未知类型的lambda?

时间:2015-12-02 07:15:22

标签: c# reflection lambda

我正在尝试制作一个简单的IoC框架。

我想制作一个映射函数,从TT的实例作为其中的一部分 - get<T>

get<T>()=new Mock<T>() if T is NOT a Func
get<T>()=()=>new Mock<R>() if T is a Func<R>
get<T>()=(P1)=>new Mock<R>(P1) if T is a Func<P1,R>
get<T>()=(P1,P2)=>new Mock<R>(P1,P2) if T is a Func<P1,P2,R>
//etc.

我写了以下代码:

public override Maybe<T> get<T>()
{
    return (T)get_default_unit_testing_definition<T>();
}
Object get_default_unit_testing_definition<T>() where T :class
{
    Type type = typeof(T);
    if (!type.IsGenericType)
        return new Mock<T>();
    Type generic = type.GetGenericTypeDefinition();
    Type return_type = type.GetGenericArguments().Last();
    Type mock_type_definition = typeof(Mock<>);
    var concrete_mock_definition = mock_type_definition.MakeGenericType(return_type);
    if (generic == typeof(Func<>))
    {
        return () => Activator.CreateInstance(concrete_mock_definition);
    }
    else if (generic == typeof(Func<,>))
    {
        return p1 => Activator.CreateInstance(concrete_mock_definition, p1);
    }
    //...
    return new Mock<T>();
}

但编译器回复我: cannot convert lambda expression to type "object" because it's not a delegate type

它是.NET 3.5,所以我没有dynamic个关键字。

将lambda分配给var也是不可能的。

如何归还这些lambdas?

2 个答案:

答案 0 :(得分:1)

稍微摆弄一下,我觉得这对你有用 如果类型不是Func,则GetMock方法将返回Mock 否则它将返回一个Func&gt;如果类型是Func

public abstract class IMockMe
{
    private readonly string _a;
    private readonly string _b;

    public IMockMe()
    {

    }

    public IMockMe(string a, string b)
    {
        _a = a;
        _b = b;
    }

    public override string ToString()
    {
        return _a + " " + _b;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var mock = GetMock(typeof(IMockMe));
        Console.WriteLine(mock);

        var func = GetMock(typeof(Func<string, string, IMockMe>));
        Console.WriteLine(func);

        var del = (Delegate)func;
        mock = del.DynamicInvoke("try", "me");
        Console.WriteLine(mock);

        new Mock<IMockMe>();

        Console.ReadLine();
    }

    private static readonly Regex FuncRegex = new Regex(@"^System.Func`\d+$");
    public static object GetMock(Type type)
    {
        var mt = typeof(Mock<>);

        if (type.IsGenericType && FuncRegex.IsMatch(type.GetGenericTypeDefinition().FullName))
        {
            var args = type.GetGenericArguments();
            var returnType = args.Last();
            mt = mt.MakeGenericType(returnType);
            args = args.Take(args.Length - 1).ToArray();

            var parameters = new List<ParameterExpression>();
            for (var i = 0; i < args.Length; i++)
            {
                var name = "P" + i;
                var arg = args[i];
                parameters.Add(Expression.Parameter(arg, name));
            }

            var array = Expression.NewArrayInit(typeof(object), parameters.Select(x => (Expression)x).ToArray());

            var ci = mt.GetConstructor(new[] {typeof (object[])});
            var constructor = Expression.New(ci, array);

            var methods = typeof(Expression).GetMethods();
            var method = (from m in methods
                          where m.Name == "Lambda"
                          let ps = m.GetParameters()
                          where ps.Length == 2
                          where ps[0].ParameterType == typeof(Expression)
                          where ps[1].ParameterType == typeof(ParameterExpression[])
                          select m)
                              .First();

            var funcType = GetFuncType(mt, args);
            method = method.MakeGenericMethod(funcType);
            var expression = (Expression)method.Invoke(null, new object[] { constructor, parameters.ToArray() });
            var compile = expression.GetType().GetMethod("Compile");
            return compile.Invoke(expression, new object[0]);
        }

        mt = mt.MakeGenericType(type);
        return (Mock) Activator.CreateInstance(mt);
    }

    private static Type GetFuncType(Type mt, Type[] args)
    {
        Type result;
        switch (args.Length)
        {
            case 0:
                result = typeof(Func<>);
                break;

            case 1:
                result = typeof(Func<,>);
                break;

            case 2:
                result = typeof(Func<,,>);
                break;

            case 3:
                result = typeof(Func<,,,>);
                break;

            case 4:
                result = typeof(Func<,,,,>);
                break;

            case 5:
                result = typeof(Func<,,,,,>);
                break;

            default: throw new ArgumentOutOfRangeException("args");
        }

        var list = args.ToList();
        list.Add(mt);

        return result.MakeGenericType(list.ToArray());
    }
}

希望这就是你要找的东西:)

答案 1 :(得分:0)

我可能误解了我的任务。如果使用Moq Framework,则映射应为

get<T>()=new Mock<T>().Object if T is NOT a Func
get<T>()=()=>new Mock<R>().Object if T is a Func<R>
get<T>()=(P1)=>new Mock<R>(P1).Object if T is a Func<P1,R>
get<T>()=(P1,P2)=>new Mock<R>(P1,P2).Object if T is a Func<P1,P2,R>

由于Mock<T>本身不是T

我写的解决方案是:编辑原始解决方案非常糟糕,并且没有使用具有参数的构造函数。更新了它。

public partial class MocksProvider : ImplementationProvider
{
    static MethodInfo[] method_mocks = typeof(MocksProvider)
        .GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
        .Where(m => m.Name == "func_mock" || m.Name == "action_mock")
        .ToArray();
    static Dictionary<Type, Object> cache = new Dictionary<Type, object>();
    public override Maybe<T> get<T>()
    {
        if (cache.ContainsKey(typeof(T)))
        {
            return (T)cache[typeof(T)];
        }
        T res=null;
        try_mock_as_method(ref res);
        if(res==null)
        {
            res = new Mock<T>().Object;
        }

        cache.Add(typeof(T),res);
        return res;
    }
    static void try_mock_as_method<T>(ref T target)
    {
        Type type = typeof(T);
        if (type == typeof(Action))
        {
            Action action = () => { };
            target = (T)(Object)action;
            return;
        }
        if (!type.IsGenericType)
        {
            return;
        }
        Type[] generic_args = type.GetGenericArguments();
        Type type_definition = type.GetGenericTypeDefinition();
        foreach (MethodInfo mock_generator in method_mocks)
        {
            Type generator_return_type = mock_generator.ReturnType.GetGenericTypeDefinition();
            if (type_definition == generator_return_type)
            {
                target = (T)mock_generator.MakeGenericMethod(generic_args).Invoke(null, new object[] { });
                return;
            }
        }
    }
    static Func<T> func_mock<T>() where T : class
    {
        return () => new Mock<T>().Object;
    }
    static Func<P1, T> func_mock<P1, T>() where T : class
    {
        return (p1) => new Mock<T>(p1).Object;
    }
    static Func<P1, P2, T> func_mock<P1, P2, T>() where T : class
    {
        return (p1, p2) => new Mock<T>(p1, p2).Object;
    }
    static Func<P1, P2, P3, T> func_mock<P1, P2, P3, T>() where T : class
    {
        return (p1, p2, p3) => new Mock<T>(p1, p2, p3).Object;
    }
    static Func<P1, P2, P3, P4, T> func_mock<P1, P2, P3, P4, T>() where T : class
    {
        return (p1, p2, p3, p4) => new Mock<T>(p1, p2, p3, p4).Object;
    }
    static Action<P1> action_mock<P1>()
    {
        return (p1) => { };
    }
    static Action<P1, P2> action_mock<P1, P2>()
    {
        return (p1, p2) => { };
    }
    static Action<P1, P2, P3> action_mock<P1, P2, P3>()
    {
        return (p1, p2, p3) => { };
    }
    static Action<P1, P2, P3, P4> action_mock<P1, P2, P3, P4>()
    {
        return (p1, p2, p3, p4) => { };
    }
}

如果您的框架中Mock<T>T,那么您应该将generic_args的最后一个元素替换为类型concrete_mock_definition并使用

new[] { Activator.CreateInstance(concrete_mock_definition)}

而不是mirror_args