type.isSubclassOf(Type otherType) 是缓存还是我必须自己做?

Is type.isSubclassOf(Type otherType) cached or do I have to do that myself?

简单问题:

是 type.isSubclassOf(Type otherType) 缓存在某处,比方说 Dictionary<Tuple<Type, Type>, bool>?

如果没有,这样的电话费用是多少?

我经常检查它以保持我的代码可扩展,并将我最​​常用的方法转换成字典...

虽然它并不理想取决于实现细节,但我们可以使用 dnSpy

查看 IsSubclassOf 的代码
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>(值元组)

结果

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

ConcurrentDictionary with value tuples 提供最好的线程安全性能,如果你不打算使用来自多线程的代码,一个简单的 Dictionary with value tuples 也能很好地工作。

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

代码

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)));
        }
    },
};

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

似乎所有其他答案都太短视了。类型实际上由运行时缓存,但不在 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