指定参数以采用泛型方法表达式

时间:2015-01-30 03:55:03

标签: c# linq-expressions

我想指定一个可以接受方法的参数,而不必指定泛型参数来生成给定方法的MethodInfo。

例如,我想写这样的代码:

interface IService
{
    object AMethod(int p1, int p2);
}
IThingy<IService>() thingy;
thingy.Add(svc => svc.AMethod);

我能提供的最接近的选项是:

interface IThingy<TService>
{
    void Add1<T0, T1, TResult>(Expression<Func<TService, Func<T0, T1, TResult>>> expression);
    void Add2(Expression<Func<TService, Func<int, int, object>>> expression);
    void Add3(Expression<Action<TService>> expression);
}
thingy.Add1<int, int, object>(svc => svc.AMethod);
thingy.Add2(svc => svc.AMethod);
thingy.Add3(svc => svc.AMethod(0, 0));

Add1意味着许多Func重载,我很好,但是如果没有指定泛型参数就无法调用。

Add2不需要通用参数,但暗示每个参数签名都有特定的重载。

Add3需要调用该方法,包括伪参数。

仅供参考,我将处理表达式以获取给定方法的MethodInfo,如下所示:

MemberInfo GetMemberFromExpression<T>(Expression<ActionT>> expression)
{
    return ((MethodCallExpression)expression.Body).Method
}
GetMemberFromExpression(svc => svc.AMethod(0, 0));

1 个答案:

答案 0 :(得分:1)

将方法传递给具有调用值的表达式

您可以使用GetMemberFromExpression方法,然后只需删除Generic参数即可。如下:

static void Main(string[] args)
{
    var memberInfo1 = GetMemberFromExpression(() => Method1(10, 20));
    var memberInfo2 = GetMemberFromExpression(() => Method2());
    var memberInfo3 = GetMemberFromExpression(() => Method3("string", 15, DateTime.Now));
    Console.WriteLine(memberInfo1.Name);
    Console.WriteLine(memberInfo2.Name);
    Console.WriteLine(memberInfo3.Name);
    Console.Read();
}

public static MemberInfo GetMemberFromExpression(Expression<Action> expression)
{
    return ((MethodCallExpression)expression.Body).Method;
}

public static object Method1(int p1, int p2)
{
    return p1 + p2;
}

public static void Method2()
{
    // No return
}

public static double Method3(string p1, int p2, DateTime p3)
{
    return 10d;
}

您会看到GetMemberFromExpression将返回您传递的任何方法的MethodInfo,无论是参数类型还是返回类型。


忽略重载,调用实例和名称

如果你不关心重载,你可以使用简单的反射而不是构建表达式。 c#6中的nameof运算符比将字符串作为字符串传递(编译时检查)更好。

public static MemberInfo GetMemberInfo(Type type, string methodName)
{
    return type.GetMethod(methodName);
}

注意,此方法中没有验证检查,只是为了显示概念。

上述方法适用于任何实例或静态方法。只需传递实例类型/或静态类类型和方法名称,如下所示:

MyClass cl = new MyClass();
var methodInfo1 = GetMemberInfo(cl.GetType(), "AMethod");
var methodInfo2 = GetMemberInfo(typeof(MyClass), "AStaticMethod");

以下是MyClass方法:

class MyClass
{
    public void AMethod(int a, int b)
    {
        // instance method
    }

    public static bool AStaticMethod(bool a, bool b)
    {
        return a & b;  // static method
    }
}

这样您就不会传递任何参数,因为您只是在调查定义而不是调用。


使用带有类型而非值的表达式

这是第三种选择。这样你就有了:

  • 编译时检查,
  • 无需传递有价值的(“假”)参数,
  • 仅需要参数类型(确保重载方法有效)。

创建一个适合ActionFunc重载的类:

public class Method
{
    public static MethodInfo GetInfo<TReturn>(Func<TReturn> method)
    {
        return method.Method;
    }
    public static MethodInfo GetInfo<TP1, TReturn>(Func<TP1, TReturn> method)
    {
        return method.Method;
    }
    public static MethodInfo GetInfo<TP1, TP2, TReturn>(Func<TP1, TP2, TReturn> method)
    {
        return method.Method;
    }    
    //... Continue with some more Func overloads


    public static MethodInfo GetInfo(Action method)
    {
        return method.Method;
    }    
    public static MethodInfo GetInfo<TP1>(Action<TP1> method)
    {
        return method.Method;
    }    
    public static MethodInfo GetInfo<TP1, TP2>(Action<TP1, TP2> method)
    {
        return method.Method;
    }
    //... Continue with some more Action overloads
}

现在您只需按照以下方式获取MethodInfo

var methodInfo1 = Method.GetInfo<int, int>(cl.AMethod);
var methodInfo2 = Method.GetInfo<bool, bool, bool>(MyClass.AStaticMethod);

是的,你必须在Method类中为ActionFunc创建一堆重载,但这是你做过的一次性事情,而.NET会做与ActionFunc代表相同,以满足所有重载。