我意识到,一般来说,使用反射会产生性能影响。 (事实上,我自己并不是反思的粉丝;这是一个纯粹的学术问题。)
假设存在一些看起来像这样的类:
public class MyClass {
public string GetName() {
return "My Name";
}
}
在这里忍受我。我知道如果我有一个名为MyClass
的{{1}}实例,我可以致电x
。此外,我可以将x.GetName()
变量设置为Func<string>
。
现在这是我的问题。假设我不知道上面的类称为x.GetName
;我有一些对象MyClass
,但我不知道它是什么。我可以通过这样做来检查该对象是否有x
方法:
GetName
假设MethodInfo getName = x.GetType().GetMethod("GetName");
不为空。然后,我无法进一步检查getName
和getName.ReturnType == typeof(string)
,并且此时,我不能确定我的getName.GetParameters().Length == 0
对象所代表的方法肯定< / em>以某种方式投射到getName
?
我意识到有一个Func<string>
,我也意识到我总是创建一个MethodInfo.Invoke
,如:
Func<string>
我想我要问的是,是否有任何方法可以从 Func<string> getNameFunc = () => getName.Invoke(x, null);
对象转换为它所代表的实际方法,从而产生性能成本进程中的反射,但 之后能够直接调用该方法(通过例如MethodInfo
或类似的东西) 性能损失。
我想象的可能是这样的:
Func<string>
(我意识到这不存在;我想知道是否有喜欢它。)
答案 0 :(得分:37)
这种方式取代了我以前的答案,因为这虽然是一个稍微长一点的路线 - 给你一个快速的方法调用,并且与其他一些答案不同,它允许你通过不同的实例(如果你要去遇到同一类型的多个实例)。如果您不想这样,请查看我底部的更新(或查看Ben M的答案)。
这是一种可以满足您需求的测试方法:
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;
然后使用传入对象的Type(来自GetType())作为键,将其添加到字典中。将来,首先要检查字典中是否有现成的委托(如果是,则调用它),否则先构建它,添加它,然后调用它。
顺便说一句,这是DLR为其动态调度机制所做的事情的非常高度简化的版本(在C#术语中,当您使用'dynamic'关键字时)。
最后
如果正如少数人提到的那样,你只是想直接将Func绑定到你收到的对象上,那么你就这样做了:
[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);
}
请注意,一旦表达式树被抛弃,您需要确保(由于Ben M的评论而删除)o
保留在范围内,否则您可能会得到一些令人讨厌的结果。最简单的方法是在委托的生命周期内保持本地引用(在类实例中)。
答案 1 :(得分:15)
是的,这是可能的:
Func<string> func = (Func<string>)
Delegate.CreateDelegate(typeof(Func<string>), getName);
答案 2 :(得分:14)
这是我的答案,通过构建表达式树。与其他答案不同,结果(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();
}
}
答案 3 :(得分:8)
最简单的方法是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();
}
答案 4 :(得分:1)
您可以构建一个表达式树,表示调用此方法的lambda,然后Compile()
,以便进一步调用将与标准编译调用一样快。
或者,我很久以前根据一篇很棒的MSDN文章写了this method,这篇文章生成一个包装器,使用IL来调用任何MethodInfo
方式比使用MethodInfo.DynamicInvoke
更快,因为一旦代码生成,正常呼叫几乎没有开销。
答案 5 :(得分:0)
我的头顶方法之一就是使用动态。那么你可以这样:
if( /* This method can be a Func<string> */)
{
dynamic methodCall = myObject;
string response = methodCall.GetName();
}