我意识到,总的来说,使用反射有绩效含义。 (实际上,我本人根本不喜欢反思;这是一个纯粹的学术问题。)

假设有一些看起来像这样的课程:

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

在这里和我在一起。我知道,如果我有一个实例 MyClassx, , 我可以打电话 x.GetName(). 。此外,我可以设置一个 Func<string> 变量 x.GetName.

现在这是我的问题。假设我 知道上一类称为 MyClass;我有一些对象, x, ,但我不知道这是什么。我可以检查一下该对象是否具有 GetName 这样做的方法:

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

认为 getName 不是零。那我不能进一步检查是否 getName.ReturnType == typeof(string)getName.GetParameters().Length == 0, ,在这一点上,我不能确定我的方法代表我 getName 对象可以 确实 被铸造 Func<string>, , 不知何故?

我意识到有一个 MethodInfo.Invoke, ,我也意识到我总是可以 创造 一个 Func<string> 喜欢:

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

我想我要的是是否有任何方法 一个 MethodInfo 目的 它代表的实际方法,产生了反思的性能成本 过程, , 但 该点能够直接调用该方法(例如,例如, Func<string> 或类似的东西) 没有 绩效处罚。

我所设想的可能是这样的东西:

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

(我意识到那是不存在的;我想知道是否有什么 喜欢 它。)

有帮助吗?

解决方案

这种替代了我以前的答案,因为这是一条稍长的路线 - 给您一个快速的方法调用,与其他一些答案不同,您可以通过不同的实例(如果您遇到多个实例,相同类型)。如果您不想要,请检查我的底部更新(或查看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;

然后,您要做的就是将其添加到字典中,使用传入对象的类型(从getType())作为键。将来,您首先检查一下词典中是否有现成的代表(如果是这样),否则首先构建它,添加它,然后调用它。

顺便说一句,这是DLR为其动态调度机制所做的类型的非常简化的版本(在C#术语中,就是您使用“动态”关键字时)。

最后

如少数人提到的那样,您只想烘烤一个直接绑定到您收到的对象的函数,然后执行此操作:

[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 留在范围内,否则您可能会获得一些令人讨厌的结果。最简单的方法是保留您代表一生的本地参考(也许在课堂实例中)。 (由于本·M的评论而删除)

其他提示

是的,这是可能的:

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

请注意,这是绑定的 getNameFuncx, ,所以每个 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();
}

您可以构建代表lambda调用此方法的表达树,然后 Compile() 因此,进一步的呼叫将与标准编译的呼叫一样快。

或者,我写了 这个方法 一段时间以前,基于一篇很棒的MSDN文章,该文章生成了一个使用IL的包装器来调用任何 MethodInfo 速度比 MethodInfo.DynamicInvoke 由于一旦生成代码,因此在普通呼叫上几乎没有开销。

我的头部方法的一个是使用动态。然后您可以这样这样:

if( /* This method can be a Func<string> */)
{
    dynamic methodCall = myObject;
    string response = methodCall.GetName();
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top