时间:2021-05-28
前言
大家都知道反射伤性能,但不得不反射的时候又怎么办呢?当真的被问题逼迫的时候还是能找到解决办法的。
反射是一种很重要的技术,然而它与直接调用相比性能要慢很多,因此如何优化反射性能也就成为一个不得不面对的问题。 目前最常见的优化反射性能的方法就是采用委托:用委托的方式调用需要反射调用的方法(或者属性、字段)。
为反射得到的方法创建一个委托,此后调用此委托将能够提高近乎直接调用方法本身的性能。(当然 Emit 也能够帮助我们显著提升性能,不过直接得到可以调用的委托不是更加方便吗?)
性能对比数据
▲ 没有什么能够比数据更有说服力(注意后面两行是有秒数的)
可能我还需要解释一下那五行数据的含义:
以下是测试代码,可以更好地理解上图数据的含义:
using System;using System.Diagnostics;using System.Reflection;namespace Walterlv.Demo{ public class Program { static void Main(string[] args) { // 调用的目标实例。 var instance = new StubClass(); // 使用反射找到的方法。 var method = typeof(StubClass).GetMethod(nameof(StubClass.Test), new[] { typeof(int) }); Assert.IsNotNull(method); // 将反射找到的方法创建一个委托。 var func = InstanceMethodBuilder<int, int>.CreateInstanceMethod(instance, method); // 跟被测方法功能一样的纯委托。 Func<int, int> pureFunc = value => value; // 测试次数。 var count = 10000000; // 直接调用。 var watch = new Stopwatch(); watch.Start(); for (var i = 0; i < count; i++) { var result = instance.Test(5); } watch.Stop(); Console.WriteLine($"{watch.Elapsed} - {count} 次 - 直接调用"); // 使用同样功能的 Func 调用。 watch.Restart(); for (var i = 0; i < count; i++) { var result = pureFunc(5); } watch.Stop(); Console.WriteLine($"{watch.Elapsed} - {count} 次 - 使用同样功能的 Func 调用"); // 使用反射创建出来的委托调用。 watch.Restart(); for (var i = 0; i < count; i++) { var result = func(5); } watch.Stop(); Console.WriteLine($"{watch.Elapsed} - {count} 次 - 使用反射创建出来的委托调用"); // 使用反射得到的方法缓存调用。 watch.Restart(); for (var i = 0; i < count; i++) { var result = method.Invoke(instance, new object[] { 5 }); } watch.Stop(); Console.WriteLine($"{watch.Elapsed} - {count} 次 - 使用反射得到的方法缓存调用"); // 直接使用反射调用。 watch.Restart(); for (var i = 0; i < count; i++) { var result = typeof(StubClass).GetMethod(nameof(StubClass.Test), new[] { typeof(int) }) ?.Invoke(instance, new object[] { 5 }); } watch.Stop(); Console.WriteLine($"{watch.Elapsed} - {count} 次 - 直接使用反射调用"); } private class StubClass { public int Test(int i) { return i; } } }}如何实现
实现的关键就在于 MethodInfo.CreateDelegate 方法。这是 .NET Standard 中就有的方法,这意味着 .NET Framework 和 .NET Core 中都可以使用。
此方法有两个重载:
他们的区别在于前者创建出来的委托是直接调用那个实例方法本身,后者则更原始一些,真正调用的时候还需要传入一个实例对象。
拿上面的 StubClass 来说明会更直观一些:
private class StubClass{ public int Test(int i) { return i; }}前者得到的委托相当于 int Test(int i) 方法,后者得到的委托相当于 int Test(StubClass instance, int i) 方法。(在 IL 里实例的方法其实都是后者,而前者更像 C# 中的代码,容易理解。)
单独使用 CreateDelegate 方法可能每次都需要尝试第一个参数到底应该传入些什么,于是我将其封装成了泛型版本,增加易用性。
using System;using System.Linq;using System.Reflection;using System.Diagnostics.Contracts;namespace Walterlv.Demo{ public static class InstanceMethodBuilder<T, TReturnValue> { /// <summary> /// 调用时就像 var result = func(t)。 /// </summary> [Pure] public static Func<T, TReturnValue> CreateInstanceMethod<TInstanceType>(TInstanceType instance, MethodInfo method) { if (instance == null) throw new ArgumentNullException(nameof(instance)); if (method == null) throw new ArgumentNullException(nameof(method)); return (Func<T, TReturnValue>) method.CreateDelegate(typeof(Func<T, TReturnValue>), instance); } /// <summary> /// 调用时就像 var result = func(this, t)。 /// </summary> [Pure] public static Func<TInstanceType, T, TReturnValue> CreateMethod<TInstanceType>(MethodInfo method) { if (method == null) throw new ArgumentNullException(nameof(method)); return (Func<TInstanceType, T, TReturnValue>) method.CreateDelegate(typeof(Func<TInstanceType, T, TReturnValue>)); } }}泛型的多参数版本可以使用泛型类型生成器生成,我在 生成代码,从 <T> 到 <T1, T2, Tn> —— 自动生成多个类型的泛型 - 吕毅 一文中写了一个泛型生成器,可以稍加修改以便适应这种泛型类。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
委托是一个类型。C#中的委托是面向对象的,并且它是类型安全的当创建委托实例的时候,创建的实例会包含一个调用列表,在调用列表中可以包含多个方法。每个方法称作一个调
反射调用返回复杂对象的.NET方法定义数据接口上一篇在C++中反射调用.NET(一)中,我们简单的介绍了如何使用C++/CLI并且初步使用了反射调用.NET程序
可能天猫直通车的浏览次数放大就比较难,到后期优化的时候,都可以提高关键词定价,大幅度降低消费群体溢价,就是把消费群体放大,门槛放低,给到更多的消费群体大幅度降低
你没看错,今日的文章标题是用直播助手大幅度提高转换,而不是大幅度提升流量,直播间是一种新式的商品呈现方式,让消费者更全方位的去掌握商品,而引流方法便是淘宝直通车
你没看错,今日的题目是用直播助手大幅度提高转换,而不是大幅度提升流量,直播间是一种新式的商品呈现方式,让消费者更全方位的去掌握商品,而引流方法便是淘宝直通车的事