C#的类型操作符的效率(或者它在MSIL中的表示形式)

pyo*_*yon 16 c#

我知道过早的优化是所有邪恶的母亲.但是,我正在定义一个泛型方法,它使用Reflection来检索其泛型类型的元数据,并想知道是否typeof(T)多次调用,如下面的代码片段所示:

private static Dictionary<Type, PropertyInfo[]> elementProperties;

private static T MakeElement<T>(SqlDataReader reader) where T : class, new() {
    PropertyInfo[] properties;
    if (elementProperties.ContainsKey(typeof(T)))
        properties = elementProperties[typeof(T)];
    else
        properties = elementProperties[typeof(T)] = typeof(T).GetProperties();

    // more code...
}
Run Code Online (Sandbox Code Playgroud)

...比将类型对象存储到变量中的效率低,如下面的代码片段所示:

private static Dictionary<Type, PropertyInfo[]> elementProperties;

private static T MakeElement<T>(SqlDataReader reader) where T : class, new() {
    PropertyInfo[] properties;
    Type type = typeof(T);
    if (elementProperties.ContainsKey(type))
        properties = elementProperties[type];
    else
        properties = elementProperties[type] = type.GetProperties();

    // more code...
}
Run Code Online (Sandbox Code Playgroud)

...?

如果我正确理解编译器理论(我认为我这样做),这个问题可以简化为以下问题:

当JIT编译器实例化泛型类型时,它是否替换了[无论MSIL表示typeof(T)形式]的每个实例...

  1. ...对实际类型对象的引用?(好)
  2. ...一个方法调用/子程序/什么检索对实际类型对象的引用?(坏)
  3. ...一个方法调用/子例程/什么构造一个类型对象并返回对它的引用?(非常非常糟糕)

use*_*116 5

生成的 MSIL 显示两者不同,typeof(T)没有被提升到局部变量中。这意味着它将T把 的类型元数据加载到堆栈上并Type.GetTypeFromHandle在每次使用时调用它。我不知道为什么它选择不使用 来提升它/optimize+,但我认为这是编译器的特权。

两个代码块之间的一个实际区别是,它基本上typeof(T)是一个常量表达式,而局部变量是可变的。这可能不是未来开发人员可能会破坏的预期语义。type


Bra*_*etz 5

有点直觉应该告诉你,声明一个变量的单个实例来保存GetType()的结果,然后在整个rest方法中使用它将更有效(并且更具可读性)

这是两种方法的IL:

MakeElement 1:

Icall       System.Type.GetTypeFromHandle
callvirt    System.Collections.Generic.Dictionary<System.Type,System.Reflection.PropertyInfo[]>.ContainsKey
brfalse.s   IL_002F
ldarg.0     
ldfld       elementProperties
ldtoken     01 00 00 1B 
call        System.Type.GetTypeFromHandle
callvirt    System.Collections.Generic.Dictionary<System.Type,System.Reflection.PropertyInfo[]>.get_Item
pop         
br.s        IL_0053
ldarg.0     
ldfld       elementProperties
ldtoken     01 00 00 1B 
call        System.Type.GetTypeFromHandle
ldtoken     01 00 00 1B 
call        System.Type.GetTypeFromHandle
call        System.Type.GetProperties
callvirt    System.Collections.Generic.Dictionary<System.Type,System.Reflection.PropertyInfo[]>.set_Item
Run Code Online (Sandbox Code Playgroud)

MakeElement 2:

call        System.Type.GetTypeFromHandle
stloc.0     
ldarg.0     
ldfld       elementProperties
ldloc.0     
callvirt    System.Collections.Generic.Dictionary<System.Type,System.Reflection.PropertyInfo[]>.ContainsKey
brfalse.s   IL_0028
ldarg.0     
ldfld       elementProperties
ldloc.0     
callvirt    System.Collections.Generic.Dictionary<System.Type,System.Reflection.PropertyInfo[]>.get_Item
pop         
br.s        IL_003A
ldarg.0     
ldfld       elementProperties
ldloc.0     
ldloc.0     
callvirt    System.Type.GetProperties
callvirt    System.Collections.Generic.Dictionary<System.Type,System.Reflection.PropertyInfo[]>.set_Item
Run Code Online (Sandbox Code Playgroud)

通过在局部变量中声明它来保存对System.Type.GetTypeFromHandle的1或2个调用.我不确定JIT的过程是不会编译出来的,但我个人会更加相信编译器来优化IL,就像JIT'er那样,但那只是我.