views:

100

answers:

2

Each item has an interface, IItem. As well as this, there is a interface known as IDrawableItem which inherits from Item.

The code below, is trying to draw a drawable item, but cannot as the collection this class stores accepts only IItem. You can add anything that inherits from IItem to this class, but using other methods can only be achieved by casting.

foreach (var item in Items) {
    item.Draw();               // The casting would go here.
}

I know how to cast, as etc... but is this acceptable? Is it a best practice?

Just wondering if there are other ways to handle such scenarios.

+11  A: 

Use Enumerable.OfType to extract only those elements of Items that implement IDrawableItem:

foreach(var item in Items.OfType<IDrawableItem>()) {
    item.Draw();
}

To address the question that Nanook asked in the comments the above code will probably be translated into code equivalent to the following:

foreach(var item in Items) {
     if(item is IDrawableItem) {
        ((IDrawable)item).Draw();
     }
}

Of course, really there is an iterator behind the scenes that looks something like this:

public static IEnumerable<T> OfType<T>(this IEnumerable<TSource> source) {
    if(source == null) {
        throw new ArgumentNullException("source");
    }
    foreach(TSource item in source) {
        if(item is T) {
            yield return (T)item;
        }
    }
}

So, what this shows is that we probably only iterate through Items once. Of course, there is no requirement that OfType is implemented like the above but this is the sensible thing to do.

Jason
Does this example actually perform 2 loops? 1 to get the IDrawableItems and another to loop through them and call Draw. If so, this is not very performant.
Nanook
No, it streams the results.
Jon Skeet
Nop, it doesn't. All Linq.Enumerable methods are implemented by using a custom Enumerator. The code above would translate into something like foreach (var item in items) { if item is IDrawableItem yield return item as IDrawableItem; }
herzmeister der welten
Excellent, just what I needed. So while it is the same as my original plan to cast behind the scenes - OfType<> is much nice way of doing it. Thanks.
Finglas
+1  A: 

Two alternative solutions:

  • Keep the drawables in a separate collection.
  • Add DrawIfPossible() method to IItem. IDrawableItem should override it to call Draw(), other IItem() implementors should have an empty implementation.

Explicit type querying is considered a sign that there might be something wrong with the design.

Rafał Dowgird
As I've said, this is a quick prototype which is likely to change. Though I do agree about your last point regarding the design.
Finglas
Wasn't meant as critique. There are cases when type querying is necessary. Quick hacks, frameworks passing objects as `ICastItDownAsNeeded`, etc.
Rafał Dowgird