C# · 12月 27, 2021

在C#中有效地使用反射

我正在C#中编写一个库,以后我将会使用一个应用程序,我希望这个库能够尽可能的高效(即不要过多地复杂一些事情,使它更有效率).但是,我有这个问题,关于如何最有效地使用反思在一个类/方法,并说明我已经简化了我的课程这个问题: class MyClass{ private static Dictionary<string,object> methods; public void Method1() { // Do something. } public void Method2() { // Do something else. }}

现在,我想要的是从类(一个私有方法尚未被创建),取一个包含method-name的字符串,然后点击该方法,简单的.这样做最简单的方法就是查看名称,获取一个名称的方法,然后执行它,但这迫使我多次使用反射.这种私有方法可能被称为数千或数万次,而且需要快速.所以我想出了两个可能的解决方案.你可能会看到我添加了一个包含string->对象的静态字典(用实际类型替换对象,只是写了对象,因为这两个我的例子).然后我会添加一个静态构造函数,通过该类,并将所有的methodinfos添加到方法字典中.然后提出一个问题,在创建一个类的新实例时,我应该为方法创建绑定的代理,并将它们放在非静态私有dict中,或者我应该简单地使用方法字典中的MethodInfo来激发方法?

平均用例将创建10个此类的实例,并且有1000个调用方法,该方法应该触发方法1或方法2,具体取决于它的字符串参数(而不是,switch-case不是一个选项,因为可扩展性如上所述,这是一个简化版本).实现这一目标最有效的方法是什么?

解决方法 显然没有人可以在没有实际尝试的情况下回答这个问题,并进行性能测试,看看你的目标是否得到满足.

反思在现代版本的框架中比以前更快,但它仍然不如简单地调用代理那么快.

我的建议是从您提出的解决方案开始:构建方法信息的缓存一次:

class MyClass{ static Dictionary<string,MethodInfo> cache = new … public void InvokeByName(string name) { MethodInfo methodInfo = getmethodInfoFromCache(name); methodInfo.Invoke(this,new object[] {}); }

当被要求调用由特定实例上的字符串标识的方法作为接收方时,按名称查找方法信息,然后使用给定的接收方调用它.衡量其性能,看看它是否符合您的目标.如果它是,伟大的不要浪费任何更多的宝贵时间,试图使更快的速度已经够快了.

如果这还不够快,那么我会做什么:

class MyClass{ static Dictionary<string,Action<MyClass>> cache = new … public void InvokeByName(string name) { GetActionFromCache(name).Invoke(this); }

那么GetActionFromCache做什么呢?如果缓存中已经有一个动作,我们就完成了.如果没有,则通过Reflection获取MethodInfo.然后使用Expression Tree库构建一个Lambda:

var methodInfo = SomehowGetTheMethodInfo(name);// We’re going to build the lambda (MyType p)=>p.<named method here>() var p = Expression.Parameter(typeof(MyType),”p”));var call = Expression.Call(p,methodInfo);var lambda = Expression.Lambda<Action<MyType>>(call,p);var action = lambda.Compile();

现在你有一个可以用实例调用的动作.把东西放在缓存中.

顺便提一句,这是一个令人难以置信的简化的水平,C#4中的“动态”是如何工作的.我们的问题是非常复杂的,因为我们必须处理接收者和任何类型的参数.你比较容易.