使用反射调用带约束的泛型方法

问题描述 投票:2回答:3

我使用Reflection从泛型方法中检索methodInfo:

public abstract class BaseIdea {}    

public class ModuleBaseLogic {
  public void Sponsor<T>(int id) {
    // Do something
  }
}

public class Caller {
  protected static MethodInfo GetMethod<T>(Expression<Action<T>> expr)
  {
    return ((MethodCallExpression)expr.Body).Method.GetGenericMethodDefinition();
  }

  public Caller() { 
    MethodInfo method = GetMethod<ModuleBaseLogic>(q => q.Sponsor<object>(id));
  }
}

这很好用。但是,如果该方法具有以下约束:

public void Sponsor<T>(int id)  where T : BaseIdea, new() {
  // Do something
}

q.Sponsor<object>(在Caller里面)不编译:

类型“对象”不能用作通用类型或方法“ModuleBaseLogic.Sponsor(int)”中的类型参数“T”。没有从“对象”到“BaseIdea”的隐式引用转换。

我尝试用q.Sponsor<BaseIdea>替换它,但这也不起作用

c# generics reflection constraints
3个回答
2
投票

这应该工作到目前为止:

public abstract class BaseIdea {}    

public class ConcreteIdea : BaseIdea {}


public class ModuleBaseLogic {
  public void Sponsor<T>(int id) 
  where T : BaseIdea, new() 
  {
    // Do something
  }
}

public class Caller {
  protected static MethodInfo GetMethod<T>(Expression<Action<T>> expr)
  {
    return ((MethodCallExpression)expr.Body).Method.GetGenericMethodDefinition();
  }

  public Caller() { 
    int id = 1;
    MethodInfo method = GetMethod<ModuleBaseLogic>(q => q.Sponsor<ConcreteIdea>(id));
  }
}

为了给出一些解释:像Jon Skeet提到的对象不能是具有任何通用约束的任何方法的通用参数。 BaseIdea不能是通用参数,因为它是抽象的,这通常是基类所必需的。最简单的Parameter是一个具体的类,它派生自BaseIdea,它是用我的ConcreteIdea-class给出的。正如kara所提到的,new()约束也需要一个无参数构造函数,它也可以是隐式构造函数(默认构造函数)。


2
投票

这里有一些例子,如果你有一个where T : SomeClass, new(),什么是允许的,什么不是

public abstract class MyBaseClass { }
public class MyClass : MyBaseClass { }
public class MyClass2 : MyBaseClass
{
    public MyClass2(int x)
    {

    }
}

public class SomeOtherClass { }

public static void MyMethod<T>() where T : MyBaseClass, new() { }

public static void Main(string[] args)
{
    MyMethod<MyBaseClass>(); // Does not work because MyBaseClass is abstract
    MyMethod<MyClass>(); // works because T is MyClass, which is derived from MyBaseClass
    MyMethod<MyClass2>(); // Doesn't work because it doesn't have a Std.Constructor "MyClass2()" it has a constructor with parameters "MyClass2(int x)"
    MyMethod<SomeOtherClass>(); // Doesn't work because T is SomeOtherClass, and it's not derived from MyBaseClass.
}

2
投票

另一种方法是使用一个表达式,通过使用nameof()运算符确定方法的名称,然后实际执行此表达式。

public class Caller
{
    protected static MethodInfo GetMethod<T>(Expression<Func<T, string>> expr) where T: class
    {
        // Execute the expression. We will get the method name.
        string methodName = expr.Compile()(null);

        // Return generic method information by name of the method
        return typeof(T).GetMethod(methodName);
    }

    public Caller()
    {
        MethodInfo method = GetMethod<ModuleBaseLogic>(m => nameof(m.Sponsor));
    }
}

注意:当使用相同名称的方法有多个重载时,这将不起作用。

© www.soinside.com 2019 - 2024. All rights reserved.