高效的运行时类型检查

发布于 2024-11-13 23:44:11 字数 535 浏览 2 评论 0原文

我有如下的类层次结构(实际上我有超过 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

疏忽 2024-11-20 23:44:11

在我看来,您应该将“某物”移动到 A 上的虚拟方法中,然后分别移动到 BC上D 可以根据需要重写它(这可能只是意味着调用外部方法 - 他们不需要自己完成工作) - 或根据需要重写。

那么这就变成:

foreach (A obj in collection)
{
    obj.DoSomething();
}

ie

class A {
    public virtual void DoSomething() {...}
}
class B : A   {
    public override void DoSomething() {...}
}

It sounds to me like you should move the "something" into a virtual method on A, then each of B, C and D 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:

foreach (A obj in collection)
{
    obj.DoSomething();
}

i.e.

class A {
    public virtual void DoSomething() {...}
}
class B : A   {
    public override void DoSomething() {...}
}

etc

相思故 2024-11-20 23:44:11

使函数“something”中实现的功能成为 A 类的行为/方法,然后在适用的子类中重新定义它(确保该方法被定义为虚拟)。

在这种情况下,您可以立即致电:

List<A> collection = ...
foreach (A obj in collection)
{
  obj.something()
}

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:

List<A> collection = ...
foreach (A obj in collection)
{
  obj.something()
}
难得心□动 2024-11-20 23:44:11

使用 asis 没有明显的性能差异。然而,我在框架中看到的做法是,它们为每种类型提供了一个具有唯一值的枚举。这样您就可以切换对象的类型。

表达式树就是这样做的,它们有一个名为 NodeType 的属性,该属性返回包含该类型的值。这样您就不需要进行多次类型测试。

马克再次提醒我,这些类型的情况有时可以通过正确使用多态性来解决,并且您遇到的这个问题可能表明您编写类的方式存在潜在问题。

There's not a tangible performance difference in using as or is. 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.

眼波传意 2024-11-20 23:44:11

在我看来,你应该只使用虚拟方法。它将产生更高效且更具可读性的代码。

It seems to me that you should just use a virtual method. It will result in efficient code that's more readable too.

み格子的夏天 2024-11-20 23:44:11

仅使用 as 并检查 null:

C c = obj as C;
if (c != null)
    something(c);

它只会执行一次转换。 asis 实际上都执行强制转换,因此实际上没有必要将它们一起使用。

话虽这么说,铸造相对便宜。与 something(c)something(b) 的实现相比,转换带来的任何性能损失都应该是小巫见大巫,所以不要过度考虑它,除非您尝试的类型数量投射到确实很重要。

如果您可以控制 ABC 类,请查看是否可以重新设计模型,这样就不需要进行转换完全 - 正如其他人建议的那样使用虚拟方法。

Use only as and check for null instead:

C c = obj as C;
if (c != null)
    something(c);

It will only perform casting once. Both as and is 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) or something(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.

七分※倦醒 2024-11-20 23:44:11

通常干净的解决方案是虚拟方法。但有时这是不可能的。在这种情况下,您可以使用Dictionary

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 的逻辑。

显然这个实现不是线程安全的。

Usually the clean solution is a virtual method. But sometimes that's not possible. In that case you could use a 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();

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 inside GetActionForType.

And obviously this implementation is not threadsafe.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文