高效的运行时类型检查

时间:2011-06-05 09:15:15

标签: c# .net .net-2.0 runtime

我有类的层次结构,如下所示(实际上我有超过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);
    ....
}

如您所见,代码会对对象类型和强制类型执行许多检查。对于具有许多元素的集合,代码的性能不是那么好。

在我的情况下,您建议加快运行时类型检查?

6 个答案:

答案 0 :(得分:11)

听起来我应该将“某些内容”移到A上的虚拟方法中,然后BCD中的每一个都可以将其覆盖为他们需要(这可能仅仅意味着调用外部方法 - 他们不需要自己完成工作) - 或者根据需要覆盖。

然后这就变成了:

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

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

答案 1 :(得分:4)

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

在这种情况下,您可以直接致电:

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

答案 2 :(得分:2)

仅使用as并检查null:

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

它只会执行一次铸造。 asis实际上都执行了转换,因此实际上不需要一起使用它们。

话虽如此,铸造相对便宜。执行something(c)something(b)时,任何对施法的性能惩罚都应该相形见绌,所以除非你尝试施放的类型数量非常重要,否则不要过度思考。

如果您掌控ABC课程,请查看您是否可以修改模型,以便根本不需要进行投射 - 使用其他人提出的虚拟方法。

答案 3 :(得分:2)

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

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

然后,Marc再次提醒我,这些类型的情况有时可以通过正确使用多态来解决,并且您遇到此问题可能是您编写类的方式存在潜在问题的迹象。 / p>

答案 4 :(得分:2)

在我看来,你应该只使用虚拟方法。这将导致更高效的代码也更具可读性。

答案 5 :(得分:2)

通常,清洁解决方案是一种虚拟方法。但有时这是不可能的。在这种情况下,您可以使用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();

请注意,这仅适用于确切类型,而不适用于派生类型。因此,您可能需要在Type.IsAssignableFrom内添加一些基于GetActionForType的逻辑。

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

相关问题