高效的运行时类型检查
本文关键字:检查 类型 运行时 高效 | 更新日期: 2023-09-27 17:58:21
我有如下的类层次结构(事实上我有3个以上的派生类型):
class A {};
class B : A {};
class C : B {};
class D : A {};
这些类的实例存储在List<A>
集合中。有时收藏品相当大(数千甚至数万件物品)。
在我的代码中,我经常需要根据对象的确切类型执行一些操作。类似这样的东西:
List<A> collection = ...
foreach (A obj in collection)
{
if (obj is C)
something(obj as C);
else if (obj is B)
somethingElse(obj as B);
....
}
正如您所看到的,代码对对象和类型转换执行了许多检查。对于包含许多元素的集合,代码的性能并不是很好。
在我的情况下,你建议如何加快运行时类型检查?
在我看来,你应该将"某物"移动到A
上的虚拟方法中,然后B
、C
和D
中的每一个都可以根据需要覆盖它(这可能只是意味着调用外部方法-他们不需要自己做工作)-或者不根据需要覆盖。
然后变成:
foreach (A obj in collection)
{
obj.DoSomething();
}
即
class A {
public virtual void DoSomething() {...}
}
class B : A {
public override void DoSomething() {...}
}
etc
将函数"something"中实现的功能作为a类的行为/方法,然后在适用的子类中重新定义它(确保该方法定义为虚拟)。
在这种情况下,您可以直接致电:
List<A> collection = ...
foreach (A obj in collection)
{
obj.something()
}
使用as
或is
没有明显的性能差异。然而,我在框架中看到的是,它们为每个类型提供了一个具有唯一值的枚举。通过这种方式,您可以打开对象的类型。
表达式树可以做到这一点,它们有一个名为NodeType
的属性,该属性返回一个包含该类型的值。这样,您就不需要进行多个类型测试。
再说一遍,Marc只是提醒我,这些类型的情况有时可以通过正确使用多态性来解决,而你遇到这个问题可能是你编写类的方式存在潜在问题的迹象。
在我看来,您应该只使用一个虚拟方法。它将产生更可读的高效代码。
仅使用as
并检查空值:
C c = obj as C;
if (c != null)
something(c);
它将只执行一次铸造。as
和is
实际上都执行选角,因此实际上不需要将它们一起使用。
也就是说,铸造相对便宜。与something(c)
或something(b)
的实现相比,强制转换的任何性能损失都应该相形见绌,所以不要想得太多,除非您尝试强制转换的类型数量非常大。
如果您控制着A
、B
、C
类,请查看是否可以重新修改您的模型,这样您就根本不需要进行强制转换——按照其他人的建议使用虚拟方法。
通常干净的解决方案是一个虚拟方法。但有时这是不可能的。在这种情况下,您可以使用Dictionary<Type,Action>
。
Dictionary<Type,Action> actions=new Dict...;
Action action;
if(!actions.TryGetValue(obj.GetType(), out action))
{
action=GetActionForType(obj.GetType());
actions.Add(obj.GetType(), action);
}
action();
请注意,这只适用于精确类型,而不适用于派生类型。因此,您可能需要在GetActionForType
中添加一些基于Type.IsAssignableFrom
的逻辑。
显然,这种实现是不安全的。