是否缓存了type.isSubclassOf(Type otherType)或我自己必须这样做?

时间:2018-02-19 12:46:06

标签: c# dictionary caching type-conversion subclass

简单问题:

是type.isSubclassOf(Type otherType)缓存为一个让我们说Dictionary<Tuple<Type, Type>, bool>

如果没有,这样的电话有多贵?

我经常检查这一点,以保持我的代码可扩展性,并将我最​​常使用的方法转换为字典...

3 个答案:

答案 0 :(得分:3)

虽然不理想,但依赖于实施细节,我们可以使用IsSubclassOf

查看dnSpy的代码
public virtual bool IsSubclassOf(Type c)
{
    Type type = this;
    if (type == c)
    {
        return false;
    }
    while (type != null)
    {
        if (type == c)
        {
            return true;
        }
        type = type.BaseType;
    }
    return false;
}

所以简短的回答是,在这个版本的框架(4.6)中,调用没有被缓存,它意味着走上继承层次结构。

电话的费用取决于您的使用案例。您应该测量您的代码是否在此方法中花费了大量时间以及缓存是否有帮助。

效果

是否值得缓存结果的问题是测量调用所花费的时间与缓存查找之间的关系。我测试了5个场景:

  1. 直接调用
  2. 使用缓存:Dictionary<Tuple<Type, Type>, bool>
  3. 缓存使用:Dictionary<(Type, Type), bool>(值元组)
  4. 使用缓存:ConcurrentDictionary<Tuple<Type, Type>, bool>
  5. 缓存使用:ConcurrentDictionary<(Type, Type), bool>(值元组)
  6. <强> 结果

    1. 直接调用 - 0.15s / call
    2. 缓存使用:Dictionary<Tuple<Type, Type>, bool> - 0.12s / call
    3. 缓存使用:Dictionary<(Type, Type), bool> - 0.06s / call
    4. 缓存使用:ConcurrentDictionary<Tuple<Type, Type>, bool> - 0.13s / call
    5. 缓存使用:ConcurrentDictionary<(Type, Type), bool>(值元组) - 0.7s / call
    6. 具有值元组的

      ConcurrentDictionary提供了最佳的线程安全性能,如果您不打算使用来自多个线程的代码,则具有值元组的简单Dictionary也可以很好地工作。

      通常,缓存只会将调用时间减半,并且不会对缓存中的大量数据执行测试,因此性能可能会因更多类而降低。我不认为值得缓存结果。

      <强> 代码

      Dictionary<Tuple<Type, Type>, bool> cache = new Dictionary<Tuple<Type, Type>, bool>();
      Dictionary<(Type, Type), bool> cache4 = new Dictionary<(Type, Type), bool>();
      ConcurrentDictionary<Tuple<Type, Type>, bool> cache2 = new ConcurrentDictionary<Tuple<Type, Type>, bool>();
      ConcurrentDictionary<(Type, Type), bool> cache3 = new ConcurrentDictionary<(Type, Type), bool>();
      var p = new Dictionary<string, Action>()
      {
          { "no chache", ()=> typeof(F).IsSubclassOf(typeof(A)) },
          {
              "dic cache", ()=>
              {
                  var key = Tuple.Create(typeof(F),typeof(A));
                  if(!cache.TryGetValue(key, out var value))
                  {
                      cache.Add(key, typeof(F).IsSubclassOf(typeof(A)));
                  }
              }
          },
          {
              "vtuple + dic cache", ()=>
              {
                  var key = (typeof(F),typeof(A));
                  if(!cache4.TryGetValue(key, out var value))
                  {
                      cache4.Add(key, typeof(F).IsSubclassOf(typeof(A)));
                  }
              }
          },
          {
              "concurrent dic cache", ()=>
              {
                  cache2.GetOrAdd(Tuple.Create(typeof(F),typeof(A)), (k)=> typeof(F).IsSubclassOf(typeof(A)));
              }
          },
          {
              "vtuple + concurrent + dic cache", ()=>
              {
                  cache3.GetOrAdd((typeof(F),typeof(A)), (k)=> typeof(F).IsSubclassOf(typeof(A)));
              }
          },
      };
      

答案 1 :(得分:3)

它不会自动缓存。您将不得不权衡呼叫费用与缓存所需的内存。我希望如果要进行很多这样的检查,字典中的缓存会提高性能。

答案 2 :(得分:2)

似乎所有其他答案都太过于近视。类型由运行时实际缓存,但在Type.BaseType级别没有。

在这样的情况下,实际上很容易弄清楚是否有任何缓存。

考虑以下代码并尝试猜测实际运行之前的输出:

public static void ToCacheOrNotToCache()
{
    var typeofA = typeof(A);
    var typeofB = typeof(B);
    var getTypeA = new A().GetType();
    var getTypeA2 = new A().GetType();
    var getTypeB = new B().GetType();
    var baseTypeB = getTypeB.BaseType;

    Console.WriteLine(
        $"typeof A ref equals getTypeA: {ReferenceEquals(typeofA, getTypeA)}");
    Console.WriteLine(
        $"typeof B ref equals getTypeB: {ReferenceEquals(typeofB, getTypeB)}");
    Console.WriteLine(
        $"typeof A ref equals baseTypeB: {ReferenceEquals(typeofA, baseTypeB)}");
    Console.WriteLine(
        $"getTypeA ref equals getTypeA2: {ReferenceEquals(getTypeA, getTypeA2)}");
}

class A { }
class B: A { }

当然,仔细检查Type.BaseType的实现应该给出足够的线索来假设某些缓存正在某种程度上进行; ==(Type, Type)是赠品; Type没有值语义,因此如果使用引用相等,则必须表示每个类型都使用一个Type的单个实例。

代码的输出当然是:

typeof A ref equals getTypeA: True
typeof B ref equals getTypeB: True
typeof A ref equals baseTypeB: True
getTypeA ref equals getTypeA2: True