Bobrovsky
Bobrovsky

Reputation: 14236

Efficient run-time type checking

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?

Upvotes: 9

Views: 430

Answers (6)

CodesInChaos
CodesInChaos

Reputation: 108790

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.

Upvotes: 2

Dyppl
Dyppl

Reputation: 12381

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.

Upvotes: 2

David Heffernan
David Heffernan

Reputation: 612794

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

Upvotes: 2

John Leidegren
John Leidegren

Reputation: 60987

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.

Upvotes: 2

Marc Gravell
Marc Gravell

Reputation: 1062502

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

Upvotes: 11

Chandu
Chandu

Reputation: 82893

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()
}

Upvotes: 4

Related Questions