你可以得到一个Func键< T> (或类似),从一个MethodInfo的对象?你可以、类似、对象、Func

2023-09-02 20:48:44 作者:花落莫相离

更新:使用前pression树使用来构建一个lambda的建议给定的的MethodInfo ,联用的防爆pression< TDelegate> .Compile 的方法,被证明是一个金矿在我的方案。在我在那里玩弄这个想法的具体情况,我看到平均执行时间为一个特定的方法从〜0.25 ms到〜0.001毫秒去:约250倍的速度增长!感谢大家的帮助!

UPDATE: The suggestion to use an expression tree to construct a lambda using the given MethodInfo, in conjunction with the Expression<TDelegate>.Compile method, proved to be a gold mine in my scenario. In the specific case where I was toying with this idea, I saw average execution time for a particular method go from ~0.25 ms to ~0.001 ms: about a 250x speed increase! Thanks to everyone for your help!

我认识到,一般来说,在使用反射的性能影响。 (我本人不是反射的粉丝可言,实际上,这是一个纯粹的学术问题。)

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

多多包涵在这里。我知道,如果我有 MyClass的的实例名为 X ,我可以叫 X。的GetName()。此外,我可以设置一个 Func键&LT;字符串&GT; 变量 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运算(字符串) getName.GetParameters()。长度== 0 ,在这一点上,我岂不​​是很肯定的是,我的的getName 对象psented的方法重新$ P $可能的绝对被转换为 Func键&LT;字符串&GT; ,不知何故

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键&LT;字符串&GT; 这样的:

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 对象的到去吧实际的方法是重新presents,招致反射在过程中的性能开销的,但是的在的这一点能够直接调用该方法(通过,例如,一个 Func键&LT;字符串&GT; 或类似的东西)的没有的性能损失。

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

(我认识到,不存在;我想知道如果有什么的喜欢的吧。)

如果我在问什么没有意义,或者如果我是不清楚,我会很高兴地试图澄清。

If what I'm asking doesn't make sense, or if I'm being unclear, I'll be happy to attempt to clarify.

推荐答案

这种取代我的previous答案,因为这一点,虽然这是一个稍长的路线 - 为您提供了一个快速的方法调用,不同于其他的答案,可以让你通过不同的情况下(如果你要遇到相同类型的多个实例)。如果你不希望出现这种情况,请查看我的更新底部(或看本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#而言,当您使用动态关键字的)。

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

但是请注意,一旦EX pression树被扔掉,你需要确保该 0 停留在范围内,否则你可以得到一些讨厌的结果。最简单的办法是留住本地引用(在一个类的实例,也许)为您的代理的生存期。(除去本·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)