Вызов универсального метода с ограничениями с использованием отражения - PullRequest
0 голосов
/ 19 февраля 2019

Я использую 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) не компилируется:

Тип "объект" не может использоваться в качестве параметра типа "T 'в универсальном типе или методе' ModuleBaseLogic.Sponsor (int) 'Не существует неявного преобразования ссылок из' object 'в' BaseIdea '.

Я пытался заменить его на q.Sponsor<BaseIdea>, но этотоже не работает

Ответы [ 3 ]

0 голосов
/ 19 февраля 2019

Это должно сработать до сих пор:

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));
  }
}

Чтобы дать небольшое объяснение: Как упоминал Джон Скит, упомянутый объект не может быть параметром Generic для любого метода с какими-либо общими ограничениями.BaseIdea не может быть универсальным параметром, потому что он абстрактный, что часто необходимо для базового класса.Простейший возможный параметр - это конкретный класс, производный от BaseIdea, который предоставляется с моим классом ConcreteIdea.Ограничение new (), как упоминалось в kara, также нуждается в конструкторе без параметров, который также может быть неявным (конструктор по умолчанию).

0 голосов
/ 19 февраля 2019

Альтернативный подход состоит в том, чтобы использовать выражение, которое определяет имя метода, используя оператор 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));
    }
}

Примечание: это не будет работать, когда естьнесколько перегрузок метода с использованием одного и того же имени.

0 голосов
/ 19 февраля 2019

Вот несколько примеров, что разрешено, а что нет, если вы получили 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.
}
...