Reputation: 14236
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
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
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
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
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
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
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