tags:

views:

122

answers:

2

How does OfType() Work?

I read this link about what's going on but how exactly does the LINQ provider know how to get all objects matching the specified type. I know the IQueryable<T> "chains" up requests and then evaluates when GetEnumerator() is called (right?).

Specifically I want to know how does the framework quickly do type comparison? I wrote a method in a .NET 2.0 project that went like this (since 2.0 doesn't support these kind of features):

    public IEnumerable<TResult> OfType<TResult>()
        where TResult : class
    {
        foreach (TItem item in this.InnerList)
        {
            TResult matchItem = item as TResult;

            if (matchItem != null)
            {
                yield return matchItem;
            }
        }
    }

Is this the best implementation?

EDIT: My main concern with this OfType<T>() is that it is fast.

A: 

It looks like a good implementation to me, but it looks kind of implementation specific (you are referring to this.InnerList). If you created an extension method (that's supported in 2.0 is it not?) that extends IEnumerable, you would be able to use it on any enumerable collection, would you not?

BlueMonkMN
The InnerList is actually a 'List<T>'. Also at work we can't use VS2008 (ugh...) so we can't target 2.0 with the 3.5 compiler. So no, I can't use an extension method.
TheCloudlessSky
+3  A: 

Your current implementation -- by design -- doesn't support value-types.

If you wanted something closer to LINQ's OfType method, that supports all types, then try this:

public IEnumerable<TResult> OfType<TResult>(IEnumerable source)
{
    foreach (object item in source)
    {
        if (item is TResult)
            yield return (TResult)item;
    }
}
LukeH
+1, that's how Linq does it.
Hans Passant
So it's faster to use `is` and then cast or is this because of your inclusion of value types? I thought using as and then checking for null is faster?
TheCloudlessSky
It's possible that the difference is irrelevant because of specific compiler optimizations.
Dave Van den Eynde
@TheCloudlessSky: If the method was constrained to only ever dealing with ref types then I would use the `as`/test-for-null combination as you've already done. The speed difference between using `is`/cast and `as`/test-for-null will be negligible, but if that level of micro-optimisation is important to you then I suggest you do some benchmarking.
LukeH
@TheCloudlessSky: (And if shaving off a few nanoseconds really is that critical then why are you using an iterator to yield an `IEnumerable<T>` sequence rather than some faster, closer-to-the-metal data type?)
LukeH