1

I'm trying to implement an alternative to OfType where you specify the desired type as a Type instance argument instead of using type parametrization but I'm not sure what the 'correct way' to implement it is. This is what I have so far:

public static IEnumerable<T> OfType<T>(this IEnumerable<T> enumerable, Type type)
{
    return enumerable.Where(element => type.IsInstanceOfType(element));
}

I initially started out with the code below, but realized it would not support inheritance...

public static IEnumerable<T> OfType<T>(this IEnumerable<T> enumerable, Type type)
{
    return enumerable.Where(element => element.GetType() == type);
}

Then I thought about comparing base-types for few seconds before thinking that would probably be a bad idea.

So my question is will the code at the top always return the same result as OfType given the types provided are the same? In other words will the collections parametrizedFiltered and typeInstanceFiltered in the code example below contain the same elements or am I way off?

IEnumerable<InterfaceType> someCollection = BlackBox.GetCollection();
IEnumerable<DerivedType> parametrizedFiltered = someCollection.OfType<DerivedType>();
IEnumerable<InterfaceType> typeInstanceFiltered = someCollection.OfType(typeof(DerivedType));
  • You might take a look at `IsAssignableFrom` for 'deep' checking with inheritence. – Jeroen van Langen Oct 06 '16 at 14:43
  • @JeroenvanLangen I think `IsAssignableFrom(x.GetType())` is equivalent to `IsInstanceOfType(x)` when you ignore the `null` case. Caching the result of `IsAssignableFrom` might improve performance though. – CodesInChaos Oct 06 '16 at 14:46
  • Array co-variance and nullable value types behave identically. Interface co-variance should work as well. – CodesInChaos Oct 06 '16 at 14:51
  • 1
    So you basically are asking if `obj is TResult` is the same as `typeof(TResult).IsInstanceOfType(obj)` – Ivan Stoev Oct 06 '16 at 14:52
  • The main difference is that `OfType` returns an `IEnumerable` of the type you specify where as what you are doing will return an `IEnumerable` of the orginial type just with the ones that don't "match" filtered. – juharr Oct 06 '16 at 14:53
  • @juharr Yes, unfortunately it is unavoidable that the IEnumerable will be of the original type but I am willing to live with that... – Sindri Jóelsson Oct 06 '16 at 15:06
  • @IvanStoev I found the linked duplicate answer a few seconds before this one was marked as duplicate... – Sindri Jóelsson Oct 06 '16 at 15:11

1 Answers1

2

If you are wondering whether your implementation will give the same results as Enumerable.OfType<> then, it will.

Enumerable.OfType<> uses is as its implementation, and as this answer shows, the is keyword will give the same results a Type.IsAssignableFrom().

The other piece of information you need is that IsInstanceOfType() is implemented using IsAssignableFrom:

public virtual bool IsInstanceOfType(Object o)
{
    if (o == null)
        return false;

    // No need for transparent proxy casting check here
    // because it never returns true for a non-rutnime type.

    return IsAssignableFrom(o.GetType());
}

The test code below will print

Demo.Base
Demo.Derived

which shows that your original code does indeed work with inheritance:

using System;
using System.Collections.Generic;
using System.Linq;

namespace Demo
{
    public class Base {}
    public class Derived : Base {}

    class Program
    {
        static void Main()
        {
            object[] array = {"String", new Base(), new Derived(), 12345};

            foreach (var item in OfType(array, typeof(Base)))
                Console.WriteLine(item);
        }

        public static IEnumerable<T> OfType<T>(IEnumerable<T> enumerable, Type type)
        {
            return enumerable.Where(element => type.IsInstanceOfType(element));
        }
    }
}

Note that because IsInstanceOfType() is implemented using IsAssignableFrom then you can write your method like so:

public static IEnumerable<T> OfType<T>(this IEnumerable<T> enumerable, Type type)
{
    return enumerable.Where(element => type.IsAssignableFrom(element.GetType()));
}
Community
  • 1
  • 1
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • 1
    By *close* you mean *identical*? – CodesInChaos Oct 06 '16 at 14:48
  • @CodesInChaos Oops I was looking at his second code sample. Yes, I guess this answer is redundant. :) Will edit. – Matthew Watson Oct 06 '16 at 14:49
  • @SindriJóelsson Since `Type.IsInstanceOfType()` will give the same results as the `is` operator, and since `Enumerable.OfType<>` uses `is` for its implementation, then the two methods will give the same results. Isn't that what you wanted? – Matthew Watson Oct 06 '16 at 14:59
  • 1
    Your question was `So my question is will the code at the top always return the same result as OfType given the types provided are the same?` and the answer therefore is `Yes, it will`, because `is` and `Type.IsInstanceOfType()` effectively work the same way. – Matthew Watson Oct 06 '16 at 15:01
  • I will mark your answer as accepted then. – Sindri Jóelsson Oct 06 '16 at 15:13