Вы можете получить Func (или аналогичный) от объекта MethodInfo?
я понимаю, что, вообще говоря, есть последствия выполнения с помощью отражения. (На самом деле я вообще не поклонник рефлексии, это чисто академический вопрос.)
предположим, что существует некоторый класс, который выглядит так:
public class MyClass {
public string GetName() {
return "My Name";
}
}
потерпи меня здесь. Я знаю, что если у меня есть экземпляр MyClass
под названием x
, Я могу назвать x.GetName()
. Кроме того, я мог бы установить Func<string>
переменная x.GetName
.
теперь вот мой вопрос. Допустим, я не знайте, что выше класс называется MyClass
; у меня есть какой-то объект, x
, но я понятия не имею, что это такое. Я мог бы проверить, есть ли у этого объекта GetName
метод сделать это:
MethodInfo getName = x.GetType().GetMethod("GetName");
предположим getName
не null. Тогда я не мог, кроме того, проверить, если getName.ReturnType == typeof(string)
и getName.GetParameters().Length == 0
, и в этот момент я не был бы совершенно уверен, что метод, представленный моим может наверняка быть приведенным к a Func<string>
, почему?
я понимаю, что есть MethodInfo.Invoke
и я также понимаю, что я всегда могла!--28-->создать a Func<string>
как:
Func<string> getNameFunc = () => getName.Invoke(x, null);
я думаю, что я спрашиваю, есть ли какой-нибудь способ пойти С a MethodInfo
объект до фактический метод, который он представляет, неся затраты на производительность отражения в
6 ответов:
этот вид заменяет мой предыдущий ответ, потому что это, хотя это немного более длинный маршрут - дает вам быстрый вызов метода и, в отличие от некоторых других ответов, позволяет вам проходить через разные экземпляры (в случае, если вы столкнетесь с несколькими экземплярами одного и того же типа). Если вы этого не хотите, проверьте мое обновление внизу (или посмотрите на ответ Бена м).
вот тестовый метод, который делает то, что вы хотите:
public class TestType { public string GetName() { return "hello world!"; } } [TestMethod] public void TestMethod2() { object o = new TestType(); var input = Expression.Parameter(typeof(object), "input"); var method = o.GetType().GetMethod("GetName", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); //you should check for null *and* make sure the return type is string here. Assert.IsFalse(method == null && !method.ReturnType.Equals(typeof(string))); //now build a dynamic bit of code that does this: //(object o) => ((TestType)o).GetName(); Func<object, string> result = Expression.Lambda<Func<object, string>>( Expression.Call(Expression.Convert(input, o.GetType()), method), input).Compile(); string str = result(o); Assert.AreEqual("hello world!", str); }
как только вы создадите делегат один раз - вы можете кэшировать его в словарь:
Dictionary<Type, Func<object, string>> _methods;
все, что вам нужно сделать, это добавить его в словарь, используя тип входящего объекта (от GetType()) в качестве ключа. В будущем вы сначала проверяете, есть ли у вас готовый делегат в словаре (и вызываете его, если да), иначе вы сначала создаете его, добавляете его, а затем вызываете его.
кстати, это очень упрощенная версия того, что DLR делает для своего динамического механизма отправки (в терминах C# это когда вы используете ключевое слово 'dynamic').
и наконец
если, как несколько человек упомянули, Вы просто хотите испечь функцию, связанную непосредственно с объектом, который вы получаете, то вы делаете это:
[TestMethod] public void TestMethod3() { object o = new TestType(); var method = o.GetType().GetMethod("GetName", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); Assert.IsFalse(method == null && !method.ReturnType.Equals(typeof(string))); //this time, we bake Expression.Constant(o) in. Func<string> result = Expression.Lambda<Func<string>>( Expression.Call(Expression.Constant(o), method)).Compile(); string str = result(); //no parameter this time. Assert.AreEqual("hello world!", str); }
обратите внимание, однако, что как только дерево выражений будет выброшено, вам нужно убедиться, что(удалено в результате комментариев Бена м)o
остается в области, в противном случае вы можете получить некоторые неприятные результаты. Самым простым способом было бы держаться за местный ссылка (в экземпляре класса, возможно) на время жизни вашего делегата.
Да, это возможно:
Func<string> func = (Func<string>) Delegate.CreateDelegate(typeof(Func<string>), getName);
вот мой ответ, построив дерево выражения. В отличие от других ответов, результат (
getNameFunc
) - это функция, которая привязана к исходному экземпляру - без необходимости передавать его в качестве параметра.class Program { static void Main(string[] args) { var p = new Program(); var getNameFunc = GetStringReturningFunc(p, "GetName"); var name = getNameFunc(); Debug.Assert(name == p.GetName()); } public string GetName() { return "Bob"; } static Func<string> GetStringReturningFunc(object x, string methodName) { var methodInfo = x.GetType().GetMethod(methodName); if (methodInfo == null || methodInfo.ReturnType != typeof(string) || methodInfo.GetParameters().Length != 0) { throw new ArgumentException(); } var xRef = Expression.Constant(x); var callRef = Expression.Call(xRef, methodInfo); var lambda = (Expression<Func<string>>)Expression.Lambda(callRef); return lambda.Compile(); } }
самый простой способ сделать это через
Delegate.CreateDelegate
:Func<string> getNameFunc = (Func<string>) Delegate.CreateDelegate( typeof(Func<string>), x, getName);
обратите внимание, что это связывает
getNameFunc
доx
, так что для каждогоx
вам нужно создать новый экземпляр делегата. Этот вариант намного проще, чемExpression
примеры. Однако с примерами на основе выражений можно создатьFunc<MyClass, string> getNameFuncForAny
один раз, который вы можете использовать для каждого экземпляраMyClass
.чтобы создать такой getNameFuncForAny, вам понадобится метод как
public Func<MyClass, string> GetInstanceMethod(MethodInfo method) { ParameterExpression x = Expression.Parameter(typeof(MyClass), "it"); return Expression.Lambda<Func<MyClass, string>>( Expression.Call(x, method), x).Compile(); }
который вы можете использовать так:
Func<MyClass, string> getNameFuncForAny = GetInstanceMethod(getName); MyClass x1 = new MyClass(); MyClass x2 = new MyClass(); string result1 = getNameFuncForAny(x1); string result2 = getNameFuncForAny(x2);
если вы не хотите быть привязанным к
Func<MyClass, string>
, можно определитьpublic TDelegate GetParameterlessInstanceMethod<TDelegate>(MethodInfo method) { ParameterExpression x = Expression.Parameter(method.ReflectedType, "it"); return Expression.Lambda<TDelegate>( Expression.Call(x, method), x).Compile(); }
вы можете построить дерево выражений, представляющее лямбда-вызов этого метода, а затем
Compile()
Это так, что дальнейшие вызовы будут так же быстро, как стандартные скомпилированные вызовы.кроме того, я писал этот метод некоторое время назад на основе большой статьи MSDN, которая генерирует оболочку с помощью IL для вызова любого
MethodInfo
намного быстрее, чем сMethodInfo.DynamicInvoke
так как после того, как код генерируется, почти нет накладных расходов по сравнению с обычным вызовом.