c#如何使用反射去创建一个委托_C# 反射的委托创建器

.Net 的反射是个很好很强大的东西,不过它的效率却实在是不给力。已经有很多人针对这个问题讨论过了,包括各种各样的 DynamicMethod 和各种各样的效率测试,不过总的来说解决方案就是利用 Expression Tree、Delegate.CreateDelegate 或者 Emit 构造出反射操作对应的委托,从而实现加速反射的目的。

虽然本篇文章同样是讨论利用委托来加速反射调用函数,不过重点并不在于如何提升调用速度,而是如何更加智能的构造出反射的委托,并最终完成一个方便易用的委托创建器 DelegateBuilder。

它的设计目标是:

能够对方法调用、构造函数调用,获取或设置属性和获取或设置字段提供支持。

能够构造出特定的委托类型,而不仅限于 Func 或者其它的 Func 和 Action,因为我个人很喜欢强类型的委托,同时类似 void MyDeleagte(params int[] args) 这样的委托有时候也是很有必要的,如果需要支持 ref 和 out 参数,就必须使用自定义的委托类型了。

能够支持泛型方法,因为利用反射选择泛型方法是件很纠结的事(除非没有同名方法),而且还需要再 MakeGenericMethod。

能够支持类型的显式转换,在对某些 private 类的实例方法构造委托时,实例本身就必须使用 object 传入才可以。

其中的 3、4 点,在前几篇随笔《C# 判断类型间能否隐式或强制类型转换》和《C# 泛型方法的类型推断》中已经被解决了,并且整合到了 PowerBinder 中,这里只要解决 1、2 点就可以了,这篇随笔就是来讨论如何根据反射来构造出相应的委托。

就目前完成的效果,DelegateBuilder 可以使用起来还是非常方便的,下面给出一些示例:

class Program {

public delegate void MyDelegate(params int[] args);

public static void TestMethod(int value) { }

public void TestMethod(uint value) { }

public static void TestMethod(params T[] arg) { }

static void Main(string[] args) {

Type type = typeof(Program);

Action m1 = type.CreateDelegate>("TestMethod");

m1(10);

Program p = new Program();

Action m2 = type.CreateDelegate>("TestMethod");

m2(p, 10);

Action m3 = type.CreateDelegate>("TestMethod");

m3(p, 10);

Action m4 = type.CreateDelegate>("TestMethod", p);

m4(10);

MyDelegate m5 = type.CreateDelegate("TestMethod");

m5(0, 1, 2);

}

}

可以说效果还是不错的,这里的 CreateDelegate 的用法与 Delegate.CreateDelegate 完全相同,功能却大大丰富,几乎可以只依靠 delegate type、type 和 memberName 构造出任何需要的委托,省去了自己反射获取类型成员的过程。

这里特别要强调一点:这个类用起来很简单,但是简单的背后是实现的复杂,所以各种没有发现的 bug 和推断错误是很正常的。

我再补充一点:虽然在这里我并不打算讨论效率问题,但的确有不少朋友对效率问题有点纠结,我就来详细解释下这个问题。

第一个问题:为什么要用委托来代替反射。如果手头有 Reflector 之类的反编译软件,可以看看 System.Reflection.RuntimeMethodInfo.Invoke 方法的实现,它首先需要检查参数(检查默认参数、类型转换之类的),然后检查各种 Flags,然后再调用 UnsafeInvokeInternal 完成真正的调用过程,显然比直接调用方法要慢上不少。而如果利用 Expression Tree 之类的方法构造出了委托,它就相当于只多了一层方法调用,性能不会损失多少(据说如果 Emit 用得好还能更快),因此才需要利用委托来代替反射。

第二个问题:什么时候适合用委托来代替反射。现在假设有一家公园,它的门票是 1 元,它还有一种终身票,票价是 20 元。如果我只是想进去看看,很可能以后就不再去了,那么我直接花 1 元进去是最合适的。但如果我想天天去溜达溜达,那么花 20 元买个终身票一定更加合适。

相对应的,1 元的门票就是反射,20 元的终身票就是委托——如果某个方法我只是偶尔调用一下,那么直接用反射就好了,反正损失也不是很大;如果我需要经常调用,花点时间构造个委托出来则是更好的选择,虽然构造委托这个过程比较慢,但它受用终身的。

第三个问题:怎么测试委托和反射的效率。测试效率的前提就是假设某个方法是需要被经常调用的,否则压根没必要使用委托。那么,基本的结构如下所示:

Stopwatch sw = new Stopwatch();

Type type = typeof(Program);

sw.Start();

Action action = type.CreateDelegate>("TestMethod");

for (int i = 0; i < 10000; i++)

{

action(i);

}

sw.Stop();

Console.WriteLine("DelegateBuilder:{0} ms", sw.ElapsedMi

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
反射C# 中一个非常强大的功能,它可以让我们在运行时动态地获取和操作对象的信息。但是由于反射需要在运行时动态解析类型和成员信息,因此会带来一定的性能损失。在需要频繁使用反射的场景下,我们需要考虑一些反射性能优化的方法。 1. 尽可能缓存反射信息 在使用反射时,我们可以将获取到的类型、方法、属性等信息缓存起来,避免重复获取。比如,我们可以使用静态变量或单例来缓存类型信息,使用委托来缓存方法调用等。这样可以避免频繁的反射操作,提高程序的性能。 2. 使用泛型方法替代反射调用 在一些场景下,我们可以使用泛型方法来替代反射调用。比如,当我们需要动态地调用某个对象的方法时,可以使用泛型方法来实现: ```csharp public static T InvokeMethod<T>(object obj, string methodName, params object[] args) { Type type = obj.GetType(); MethodInfo method = type.GetMethod(methodName); return (T)method.Invoke(obj, args); } ``` 这样就可以避免使用反射调用方法,提高程序的性能。 3. 使用 Emit 动态生成 IL 代码 在一些极端场景下,反射的性能问题可能会非常严重。这时,我们可以使用 Emit 动态生成 IL 代码来实现某些操作,比如动态生成一个类型、动态生成一个方法等。这虽然比较复杂,但可以极大地提高程序的性能。 总之,在使用反射时,我们需要尽可能地避免重复的反射操作,使用缓存和泛型方法等方法来提高程序的性能。在一些极端场景下,可以考虑使用 Emit 动态生成 IL 代码来实现更高效的操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值