tags:

views:

270

answers:

7

I'm writing a filter function to return the specific type specified out of a larger collection of supertypes (objects for example). The idea is I give you an enumerable and you return me all the strings for example. you can write it this way without generics:

public static IEnumerable Filter(IEnumerable source, Type type)
{
 List<object> results = new List<object>();

 foreach(object o in source)
 {
  if(o != null && o.GetType() == type)
  {
   results.Add(o);
  }
 }

 return results;
}

if we want to return generics there are a few different ways to go about it.

As a straight port:

public static IEnumerable<TResult> Filter<TResult>
                          (IEnumerable source, Type type)

Pass in an 'example':

IEnumerable<TResult> Filter<TResult>
    (IEnumerable source, TResult resultType)

Ultimately what I think is cleanest:

public static IEnumerable<T> Filter<T>(IEnumerable source)

The second type would be called entirely with parameters (and infer the type):

Filter(myList, "exampleString");

where as the final version there would get called with a type specifier:

Filter<string>(myList);

What is the appropriate way to strongly type the return of a generic function, where the return type isn't automatically implied in the signature? (why?)

(Edit Note: Our input is NOT typed, e.g. IEnumerable<T>. At best it would be IEnumerable. This function is returning the Ts out of the whole collection of other types.)

+7  A: 

The following extension method included in Linq does exactly what you need:

IEnumerable<T> OfType<T>(this IEnumerable enumerable);

Here is a usage example:

List<object> objects = //...

foreach(string str in objects.OfType<string>())
{
    //...
}

As you can see, they used the generic parameter as the return type specifier. This is simpler and safer than using a Type or a string and return a non type safe enumeration.

Coincoin
So the '.net' way appears to be specifying the type, that is good to know, thank you. Unfortunately we're in a 2.0 world, and we're lucky to have generics.
Colin Dabritz
Yeah, I understand, we were stuck with 2.0 for quite a while here too. I guess the nearest you could get to .Net is your last solution. The only missing thing would be the extension method.
Coincoin
I marked this as accepted on account of the cited source, good thinking.
Colin Dabritz
+1  A: 

The cleanest way would be

public static IEnumerable<T> Filter<T>(IEnumerable<T> source)

This removes all non-type safe functions. You can then convert any non-generic IEnumerable into a generic version with a cast call

IEnumerable enumerable = GetMyEnumerable();
var filtered = Filter(enumerable.Cast<string>());

You could also additionally make it an extension method and make the call even more streamline.

public static IEnumerable<T> Filter<T>(this IEnumerable<T> source)
...
var filtered = GetMyEnumerable().Cast<string>().Filter();

EDIT

OP mentioned they only want to filter to specific types. In that case you can just use Enumerable.OfType

var filtered = GetMyEnumerable().OfType<SomeType>();
JaredPar
You forgot the 'this' keyword- fixed it for you.
Joel Coehoorn
@Joel thanks! I even added it twice specifically to add that in and show the difference.
JaredPar
I'll update the first post to be more clear, but we're processing enumerations of jumbled types. IEnumerable<object> at best. We're filtering to JUST the Ts in the enum.
Colin Dabritz
Thanks for the update. OfType is a Linq extension method. We're unfortunately on 2.0, so we're lucky to have generics. Another post mentioned this, and it seems the MS standard is to specify the type.
Colin Dabritz
+4  A: 

I generally prefer the final version - it specifies all the relevant information and nothing else. Given the version with parameters, if you were new to the code wouldn't you expect the value of the parameter to be meaningful, rather than just the type?

Very occasionally this "dummy parameter" pattern is useful, but I'd generally steer clear of it - or at the very least provide an overload which didn't require it.

Jon Skeet
Jon, Thanks for answering the philosophical question verbally! If I could mark two answers as 'accepted' this would be the second. The other cited MS code in LINQ, which is more authoritative, but I appreciate the discussion as well! Thank you.
Colin Dabritz
A: 

Hi

this is my 2 cents. I'm a bit confused about what you are trying to filter. IEnumerable is non-generic, so how are you going to filter a non-generic source and return a result of IEnuerable.

I think the cleanest is

public static IEnumerable<T> Filter<T>(IEnumerable<T> source)

you can even put in generic type checks, if you know what kind of types your filter of T will be, e.g. only classes or objects of a certain interface or base class, or value types..like so.

public static IEnumerable<T> Filter<T>(IEnumerable<T> source) where T : class
public static IEnumerable<T> Filter<T>(IEnumerable<T> source) where T : struct
edfoh
I updated the first post to be more clear. I am taking the Ts out of a collection of objects. At best it would be IEnumerable<Object> on the input. You can't infer the return from that, hence the question.
Colin Dabritz
+1  A: 

If you are using framework 3.5, this is already implemented in IEnumerable:

IEnumerable<string> s = someList.OfType<string>()
Guffa
A: 

It's clear (to me, at least) that you'd want the final version:

IEnumerable<T> Filter<T>(IEnumerable source)

by process of reduction, if nothing else.

The first version:

IEnumerable<T> Filter<T>(IEnumerable source, Type type)

has to deal with craziness where I pass in a Type that does not match the constraint:

The second version:

IEnumerable<T> Filter<T>(IEnumerable source, T type)

makes me construct an example, which may be expensive or otherwise unavailable for construction. Also, what if I pass in null (in either case)?

As an aside, for single constraints, I think the type parameter should be T. You can get away with TResult if it's the return of a function - like Func<TResult>, but otherwise it's just more typing with less clarity.

Mark Brackett
A: 

I think this is pretty much what you want.

public static IEnumerable<T> OfType<T>(IEnumerable source) {
    foreach (object obj in source)
        if (obj is T)
            yield return (T)obj;
}

A slightly more complex version but (probably) slightly faster would be

public static IEnumerable<T> OfType<T>(IEnumerable source) {
    foreach (object obj in source) {
        T result = obj as T;
        if (result != null)
            yield return result;
    }
}
configurator
Use as and save the duplicate cast: http://msdn.microsoft.com/en-us/library/cscsdfbt(VS.71).aspx
Mark Brackett