Как вызвать универсальный метод во время выполнения безопасным для типов способом

#c# #generics #reflection

#c# #универсальные методы #отражение

Вопрос:

Я пытаюсь вызвать универсальный метод во время выполнения и получить рабочий код. Однако мне было интересно, есть ли лучший способ получения информации о методе, так как если я изменю имя метода, это сломается.

 public int Handle<T>(CreateCodeModel obj) where T : CodeModel
{
     //Create code here...
}

//codeType is selected by user.
public int Handle(CreateCodeModel obj, Type codeType)
{

    MethodInfo method = this.GetType().GetMethod("Handle");
    //MethodInfo method = this.GetType().GetMethods()
            .Where(mi => mi.IsGenericMethod amp;amp; mi.Name == "Handle").First();

    MethodInfo genericMethod = method.MakeGenericMethod(new Type[] { codeType });
    return (int)genericMethod.Invoke(this, new object[] { obj });

}
  

Я надеялся, что есть более удобный способ сделать это, возможно, используя действия для получения информации о методе, но тогда мне все равно нужно указать тип, например

 Action<CreateCodeModel> h = (x) => Handle(x);
MethodInfo method = h.Method;
  

Комментарии:

1. По крайней мере, вы могли бы использовать nameof , а не жестко закодированную строку. Таким образом, если метод переименован, вы получите ошибку компилятора, предполагающую, что такие инструменты, как R # или ваша IDE, не применяют рефакторинг для вас.

2. Спасибо, сделаю. Но там, где у меня есть перегруженные методы, это не вызовет ошибок компилятора, если я не переименую оба метода. Поэтому я надеялся, что есть способ использовать действие или выражение. Но, возможно, это невозможно, или я использую неправильный подход…

Ответ №1:

Здесь вы на правильном пути. Подпись вашего делегата должна соответствовать вызываемому вами методу, поэтому в данном случае это было бы Func<CreateCodeModel,int> вместо Action<CreateCodeModel> . И вы должны указать общий параметр, который соответствует общим ограничениям. Это может быть что угодно, поскольку мы просто удалим его с помощью нашего вызова GETGENERICMETHOD Definition , но мне нравится использовать класс из ограничения.

Другой фрагмент кода ниже — это идея о том, что вам нужно получить определение универсального метода только один раз, поскольку оно не меняется, поэтому я сохраняю его в статической переменной, чтобы его можно было использовать повторно.

 public int Handle<T>(CreateCodeModel obj) where T : CodeModel
{
    //Create code here...
    return 7;
}

// this static variable preserves the generic MethodInfo, so we don't have
// keep discovering it with reflection
private static MethodInfo _methodInfoForHandle;

// The generic MethodInfo only needs to be discovered the first time this runs
private MethodInfo MethodInfoForHandle
{
    get
    {
        return _methodInfoForHandle ?? (_methodInfoForHandle = GetMethodInfoForHandleMethod());
    }
}

private MethodInfo GetMethodInfoForHandleMethod()
{
    Func<CreateCodeModel, int> handleFunc = Handle<CodeModel>;
    return handleFunc.Method.GetGenericMethodDefinition();
}


//codeType is selected by user.
public int Handle(CreateCodeModel obj, Type codeType)
{
    MethodInfo genericMethod = MethodInfoForHandle.MakeGenericMethod(new Type[] { codeType });
    return (int)genericMethod.Invoke(this, new object[] { obj });

}

public class CreateCodeModel { }
public class CodeModel { }
public class JavascriptCodeModel : CodeModel { }