高效的运行时类型检查
我有如下的类层次结构(实际上我有超过 3 个派生类型):
class A {};
class B : A {};
class C : B {};
class D : A {};
这些类的实例存储在 List
集合中。有时集合非常大(数千甚至数万个对象)。
在我的代码中,我经常需要根据对象的确切类型执行一些操作。类似这样的:
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);
....
}
如您所见,代码对对象的类型和强制转换执行许多检查。对于具有许多元素的集合,代码的性能并不是那么好。
在我的情况下,您会建议什么来加快运行时类型检查?
I have hierarchy of classes like follows (in fact I have more than 3 derived types):
class A {};
class B : A {};
class C : B {};
class D : A {};
Instances of these classes are stored in List<A>
collections. Sometimes collections are quite big (thousands or even tens of thousands of objects).
In my code I frequently need to perform some actions depending on the exact type of the objects. Something like this:
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);
....
}
As you see, the code performs many checks for type of the object and casts. For collections with many elements performance of the code is not that great.
What would you recommend to speed up run time type checks in my case?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
在我看来,您应该将“某物”移动到
A
上的虚拟方法中,然后分别移动到B
、C
和上D
可以根据需要重写它(这可能只是意味着调用外部方法 - 他们不需要自己完成工作) - 或不根据需要重写。那么这就变成:
ie
等
It sounds to me like you should move the "something" into a virtual method on
A
, then each ofB
,C
andD
can override it as they need (which may just mean calling an external method - they don't need to do the work themselves) - or not override as they need.Then this becomes:
i.e.
etc
使函数“something”中实现的功能成为 A 类的行为/方法,然后在适用的子类中重新定义它(确保该方法被定义为虚拟)。
在这种情况下,您可以立即致电:
Make the functionality implemented in the function "something" a behaviour/method of Class A and then redefine it in child classes as applicable (make sure the method is defined as virtual).
In this case you can straight away call:
使用
as
或is
没有明显的性能差异。然而,我在框架中看到的做法是,它们为每种类型提供了一个具有唯一值的枚举。这样您就可以切换对象的类型。表达式树就是这样做的,它们有一个名为
NodeType
的属性,该属性返回包含该类型的值。这样您就不需要进行多次类型测试。马克再次提醒我,这些类型的情况有时可以通过正确使用多态性来解决,并且您遇到的这个问题可能表明您编写类的方式存在潜在问题。
There's not a tangible performance difference in using
as
oris
. What I've seen done in the framework however is that they provide an enum with a unique value for each type. This way you can switch on the type of the object.Expression trees does this, they have a property called
NodeType
that returns a value containing the type. This way you don't need to do more than one type test.Then again, Marc just reminded me that these type of situations can sometimes be solved by using polymorphism correctly and that you're having this problem can be a sign of an underlying problem with the way you've written your classes.
在我看来,你应该只使用虚拟方法。它将产生更高效且更具可读性的代码。
It seems to me that you should just use a virtual method. It will result in efficient code that's more readable too.
仅使用
as
并检查 null:它只会执行一次转换。
as
和is
实际上都执行强制转换,因此实际上没有必要将它们一起使用。话虽这么说,铸造相对便宜。与
something(c)
或something(b)
的实现相比,转换带来的任何性能损失都应该是小巫见大巫,所以不要过度考虑它,除非您尝试的类型数量投射到确实很重要。如果您可以控制
A
、B
、C
类,请查看是否可以重新设计模型,这样就不需要进行转换完全 - 正如其他人建议的那样使用虚拟方法。Use only
as
and check for null instead:It will only perform casting once. Both
as
andis
actually perform casting so there is really no need to use them together.That being said, casting is relatively cheap. Any performance penalties for casting should be dwarfed by the implementation of
something(c)
orsomething(b)
, so don't overthink it unless the amount of types you try to cast to is really significant.If you are in control of
A
,B
,C
classes, see if you can rework your model so you don't need to do casting at all - make use of virtual methods as others suggest.通常干净的解决方案是虚拟方法。但有时这是不可能的。在这种情况下,您可以使用
Dictionary
。请注意,这仅适用于确切类型,不适用于派生类型。因此,您可能需要在
GetActionForType
内添加一些基于Type.IsAssignableFrom
的逻辑。显然这个实现不是线程安全的。
Usually the clean solution is a virtual method. But sometimes that's not possible. In that case you could use a
Dictionary<Type,Action>
.Note that this works only on the exact types, not on derived types. So you might need to add some logic based on
Type.IsAssignableFrom
insideGetActionForType
.And obviously this implementation is not threadsafe.