【问题标题】:Specify a parameter to take a generic method expression指定参数以采用泛型方法表达式
【发布时间】:2015-02-04 16:16:57
【问题描述】:

我想指定一个可以接受方法的参数,而不必指定通用参数来生成给定方法的 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));

【问题讨论】:

  • 如果你想要的只是MethodInfo,为什么不能直接处理类型参数T?为什么是这种表达方式?
  • 处理类型参数会给我类型,但我想要方法。
  • 在您的示例中,所涉及的类型只有一种方法。因此,给定类型,该方法很容易识别。如果您的类型有不止一种方法,那么坦率地说,这只是使这个问题易于理解的众多缺失细节中的一种。似乎这就是通常被描述为"XY Problem" 的内容。 IE。你有一些其他的目标正在努力实现,而不是问如何实现那个目标,你问的是如何做一些你认为是实现该目标的正确方法的具体事情。跨度>

标签: c# linq-expressions


【解决方案1】:

将方法传递给表达式及其调用值

您可以使用现有的GetMemberFromExpression 方法,然后简单地删除通用参数。如下:

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 对 Action 也是如此和Func 代表来处理所有的重载问题。

【讨论】:

  • 谢谢。我看过那篇文章 - 这就像我的 Add3 选项。不想仅仅为了提供一种方法而指定十几个假参数。但是,这是迄今为止最好的选择。
  • 我明白了,不幸的是,我不知道您是否要绕过解决方案而不必提供假参数或至少提供参数类型。如果你忽略它们,你最终会忽略潜在的过载。例如如果您只使用名称“AMethod”,则必须以某种方式区分您所指的重载方法是否有多个。
  • 是的 - 我认为你是对的 - 在我的情况下,我们不允许在这些类上重载方法,因此指定方法名称就足够了。但我想一般来说,编译器很难应付。
  • 在我的回答中查看更新。如果您没有重载,则可以使用基本反射来获取 MethodInfo。这样您就不需要通过传递假参数来实际“调用”该方法。传递类型和方法名。
  • 这是一个我没有考虑提及的选项,因为它不提供编译时检查,这就是我喜欢表达式的原因。我认为必须可以在不指定参数或参数类型的情况下使用表达式——因为我可以理解为人类。我的结论是编译器不是那么聪明——除非我给它一个明确的签名,比如 Add2。请注意,传递表达式实际上并不调用(执行)给定的方法——它只是提供一个消费者可以处理的表达式。这就是为什么我必须提供不会被使用的参数的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-03-20
  • 1970-01-01
  • 2017-01-22
  • 1970-01-01
  • 1970-01-01
  • 2011-02-04
  • 1970-01-01
相关资源
最近更新 更多