你能得到一个 Func<T>(或类似的)来自 MethodInfo 对象? [英] Can you get a Func<T> (or similar) from a MethodInfo object?

查看:16
本文介绍了你能得到一个 Func<T>(或类似的)来自 MethodInfo 对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我意识到,一般来说,使用反射会对性能产生影响.(实际上,我本人根本不喜欢反思;这是一个纯粹的学术问题.)

I realize that, generally speaking, there are performance implications of using reflection. (I myself am not a fan of reflection at all, actually; this is a purely academic question.)

假设存在一个看起来像这样的类:

Suppose there exists some class that looks like this:

public class MyClass {
    public string GetName() {
        return "My Name";
    }
}

在这里忍受我.我知道如果我有一个名为 xMyClass 实例,我可以调用 x.GetName().此外,我可以将 Func 变量设置为 x.GetName.

Bear with me here. I know that if I have an instance of MyClass called x, I can call x.GetName(). Furthermore, I could set a Func<string> variable to x.GetName.

现在是我的问题.假设我知道上面的类叫做MyClass;我有一些对象,x,但我不知道它是什么.我可以通过执行以下操作来检查该对象是否具有 GetName 方法:

Now here's my question. Let's say I don't know the above class is called MyClass; I've got some object, x, but I have no idea what it is. I could check to see if that object has a GetName method by doing this:

MethodInfo getName = x.GetType().GetMethod("GetName");

假设 getName 不为空.然后我不能进一步检查是否 getName.ReturnType == typeof(string)getName.GetParameters().Length == 0,在这一点上,不会我很确定我的 getName 对象表示的方法肯定可以强制转换为 Func,不知何故?

Suppose getName is not null. Then couldn't I furthermore check if getName.ReturnType == typeof(string) and getName.GetParameters().Length == 0, and at this point, wouldn't I be quite certain that the method represented by my getName object could definitely be cast to a Func<string>, somehow?

我意识到有一个 MethodInfo.Invoke,而且我也意识到我总是可以创建一个 Func 像:

I realize there's a MethodInfo.Invoke, and I also realize I could always create a Func<string> like:

Func<string> getNameFunc = () => getName.Invoke(x, null);

我想我要问的是是否有任何方法可以一个MethodInfo对象它所代表的实际方法,导致过程中反射的性能成本,但之后能够直接调用该方法(例如,通过Func 或类似的东西)没有性能损失.

I guess what I'm asking is if there's any way to go from a MethodInfo object to the actual method it represents, incurring the performance cost of reflection in the process, but after that point being able to call the method directly (via, e.g., a Func<string> or something similar) without a performance penalty.

我的设想可能是这样的:

What I'm envisioning might look something like this:

// obviously this would throw an exception if GetActualInstanceMethod returned
// something that couldn't be cast to a Func<string>
Func<string> getNameFunc = (Func<string>)getName.GetActualInstanceMethod(x);

(我意识到那不存在;我想知道是否有任何类似的.)

(I realize that doesn't exist; I'm wondering if there's anything like it.)

推荐答案

这种替代了我之前的答案,因为虽然这是一条稍长的路线 - 为您提供快速方法调用,并且与其他一些答案不同,允许您要通过不同的实例(以防您遇到多个相同类型的实例).如果您不想要那样,请在底部查看我的更新(或查看 Ben M 的回答).

This kind of replaces my previous answer because this, although it's a slightly longer route - gives you a quick method call and, unlike some of the other answers, allows you to pass through different instances (in case you're going to encounter multiple instances of the same type). IF you don't want that, check my update at the bottom (or look at Ben M's answer).

这是一个可以满足您要求的测试方法:

Here's a test method that does what you want:

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

一旦您构建了委托 - 您可以将其缓存在字典中:

Once you build the delegate once - you can cache it in a Dictionary:

Dictionary<Type, Func<object, string>> _methods;

然后您要做的就是将其添加到字典中,使用传入对象的类型(来自 GetType())作为键.以后,你首先检查字典中是否有现成的委托(如果有就调用它),否则你先构建它,添加它,然后调用它.

All you do then is add it to the dictionary, using the incoming object's Type (from GetType()) as the key. In the future, you first check to see if you have a ready-baked delegate in the dictionary (and invoke it if so), otherwise you build it first, add it in, and then invoke it.

顺便说一句,这是 DLR 为其动态分派机制所做的事情的高度简化版本(在 C# 术语中,即使用dynamic"关键字时).

Incidentally, this is a very highly simplified version of the kind of thing that the DLR does for it's dynamic dispatch mechanism (in C# terms, that's when you use the 'dynamic' keyword).

最后

如果,正如一些人提到的,您只是想烘焙一个直接绑定到您收到的对象的 Func,那么您可以这样做:

If, as a few people have mentioned, you simply want to bake a Func bound directly to the object you receive then you do this:

[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 保持在范围内,否则您可能会得到一些令人讨厌的结果.最简单的方法是在您的委托的生命周期内保持本地引用(可能在类实例中).(由于 Ben M 的评论而删除)

Note, though, that once the expression tree gets thrown away, you need to make sure that the o stays in scope, otherwise you could get some nasty results. The easiest way would be to hold on to a local reference (in a class instance, perhaps) for the lifetime of your delegate. (Removed as a result of Ben M's comments)

这篇关于你能得到一个 Func&lt;T&gt;(或类似的)来自 MethodInfo 对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆