您现在的位置是:首页>博客详情

C# 之 反射性能优化2

FreshMan 2019年04月14日 10:17 C# 反射 性能优化 228

简介在上篇博客中,我介绍了优化反射的第一个步骤:用委托调用代替直接反射调用。   然而,那只是反射优化过程的开始,因为新的问题出现了:如何保存大量的委托?

问题回顾

  在上篇博客中,我介绍了优化反射的第一个步骤:用委托调用代替直接反射调用。
  然而,那只是反射优化过程的开始,因为新的问题出现了:如何保存大量的委托?

  如果我们将委托保存在字典集合中,会发现这种设计会浪费较多的执行时间,因为这种设计会引发三个新问题:
  1. 代码的执行路径变长了。
  2. 字典查找是有成本开销的。
  3. 字典集合的并发读写需要锁定,会影响并发性。

  再来回顾一下上次的测试结果吧:

  虽然通用接口ISetValue将反射性能优化了37倍,但是最终的FastSetValue将这个数字减少到还不到7倍(在CLR4中还不到5倍)。
  难道您不觉得遗憾吗?

  再看看直接调用与反射调用的对比,它们的速度相差了上千倍!

  能不能不使用委托?

  既然委托最后引出了三个难以解决的问题,导致优化后速度比直接调用差距太远,那我们能不能不使用委托呢?

  委托调用并不是优化反射的唯一方案,我们还有其它方法,
  之所以委托调用能成为常见的优化方案是因为它比较简单。

  假如我需要用客户端提交的数据来填充某个数据对象,考虑到代码的通用性,我会用反射写成这样:

/// <summary>
/// 从HttpRequest加载obj所需的数据
/// </summary>
/// <param name="request"></param>
/// <param name="obj"></param>
public static void LoadDataFromHttpRequest(HttpRequest request, object obj)
{
    PropertyInfo[] properties = obj.GetType().GetProperties();
    foreach( PropertyInfo p in properties ) {
        // 这里只是示意代码,假设数据处理不会有异常。
        object val = Convert.ChangeType(request[p.Name], p.PropertyType);
        p.FastSetValue(obj, val);
    }
}

  如果我事先知道要加载已知的数据类型,代码会写成这样:

public static void LoadDataFromHttpRequest(HttpRequest request, OrderInfo order)
{
    // 这里只是示意代码,假设数据处理不会有异常。
    order.OrderID = int.Parse(request["OrderID"]);
    order.OrderDate = DateTime.Parse(request["OrderDate"]);
    order.SumMoney = decimal.Parse(request["SumMoney"]);
    order.Comment = request["Comment"];
    order.Finished = bool.Parse(request["Finished"]);
}

  显然,第二段代码运行效率更快(尽管第一段代码调用FastSetValue优化了速度)。

  大家都知道反射性能较差,直接调用性能最好,那么能不能在运行时不使用反射呢?

  的确,使用反射是因为我们事先不知道要处理哪些类型的对象,因此不得不用反射, 另外,反射的代码也更通用,写一个方法可以加载所有的数据类型,可认为是一劳永逸的方法。 不过,就算我们事先不知道要处理哪些对象类型,但是只要使用反射,我们完全可以知道任何一个类型包含哪些数据成员, 还能知道这些数据成员的数据类型,这一点不用怀疑吧? 既然我们用反射可以知道所有的类型定义信息,我们是否可以参照代码生成器的思路去生成代码呢? 我们可以参照前面第二段代码,为【需要处理的类型】生成直接调用的代码,这样不就彻底解决了反射性能问题了吗? 生成代码的过程,其实也就是个字符串的拼接过程,难度并不大,只是比较复杂而已。