3

I have a method that looks like this:

private IEnumerable<T> QueryCollection<T>() where T : BaseObj
{
      IEnumerable<T> items = query<T>();
      return items;
}

I now have a situation where I want to filter this items collection IF the "T" supports a certain interface (it might not so i can't simply add it as a constraint of T). So i want something like this:

private IEnumerable<T> QueryCollection<T>() where T : BaseObj
{
     IEnumerable<T> items = query<T>();

     if (typeOf(T).GetInterface(ITeamFilterable) != null)
     {
           items = FilterByTeams(items);
     }
     return items;
}

What is the recommended way of checking if my generic Type supports a certain interface **and then if YES, then

  1. Use that within the method to filter a collection
  2. But still return the collection at type "T" in the overall method

NOTE: FilterByTeams takes in an:

   IEnumerable<ITeamFilterable> 

AND returns

   IEnumerable<ITeamFilterable>

Do I need to cast the collection 2 times (one to convert to List of the interface and then again to convert back to list of T?)

leora
  • 188,729
  • 360
  • 878
  • 1,366

1 Answers1

1

I'm afraid you can't use is/as in this case unless you can start enumerating the query.

You can use reflection like that:

private IEnumerable<T> QueryCollection<T>() where T : BaseObj
{
     IEnumerable<T> items = query<T>();

     if (typeof(ITeamFilterable).IsAssignableFrom(typeof(T)))
           items = (IEnumerable<T>)(object)FilterByTeams(items.Cast<ITeamFilterable>());

     return items;
}

But if you can enumerate the query, you can do that to avoid reflection (assuming you have no null items):

private IEnumerable<T> QueryCollection<T>() where T : BaseObj
{
     IEnumerable<T> items = query<T>();
     ICollection<T> itemsCollection = items as ICollection<T> ?? items.ToList();

     if (itemsCollection.Count > 0)
     {
         var firstItem = itemsCollection.First();
         if (firstItem is ITeamFilterable)
             return (IEnumerable<T>)(object)FilterByTeams(itemsCollection.Cast<ITeamFilterable>());
     }

     return itemsCollection;
}

The casts will work since ITeamFilterable is a T and IEnumerable<T> is covariant, but you have to cast to object in-between to satisfy the generics constraints. Using these casts, there's no need to copy items around.

Lucas Trzesniewski
  • 50,214
  • 11
  • 107
  • 158
  • see the update to my question. Your answer doesn't work because the FilterByTeams doesn't return collection of T but rather collection of the ITeamFilterable interface – leora Sep 28 '14 at 13:12
  • @leora Answer updated, this should work – Lucas Trzesniewski Sep 28 '14 at 13:19
  • so your answer, in short, is Yes, you need to cast back and forth – leora Sep 28 '14 at 13:52
  • In short, yes. But my point is you don't need to create a new list of `ITeamFilterable` just for the sake of having a list of `ITeamFilterable`, a cast is sufficient and does not copy data around. I think I misunderstood this aspect of your question. – Lucas Trzesniewski Sep 28 '14 at 13:57