Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I implement NotOfType<T> in LINQ that has a nice calling syntax?

I'm trying to come up with an implementation for NotOfType, which has a readable call syntax. NotOfType should be the complement to OfType<T> and would consequently yield all elements that are not of type T

My goal was to implement a method which would be called just like OfType<T>, like in the last line of this snippet:

public abstract class Animal {}
public class Monkey : Animal {}
public class Giraffe : Animal {}
public class Lion : Animal {}

var monkey = new Monkey();
var giraffe = new Giraffe();
var lion = new Lion();

IEnumerable<Animal> animals = new Animal[] { monkey, giraffe, lion };

IEnumerable<Animal> fewerAnimals = animals.NotOfType<Giraffe>();

However, I can not come up with an implementation that supports that specific calling syntax.

This is what I've tried so far:

public static class EnumerableExtensions
{
    public static IEnumerable<T> NotOfType<T>(this IEnumerable<T> sequence, Type type)
    {
        return sequence.Where(x => x.GetType() != type);
    }

    public static IEnumerable<T> NotOfType<T, TExclude>(this IEnumerable<T> sequence)
    {
        return sequence.Where(x => !(x is TExclude));
    }
}

Calling these methods would look like this:

// Animal is inferred
IEnumerable<Animal> fewerAnimals = animals.NotOfType(typeof(Giraffe));

and

// Not all types could be inferred, so I have to state all types explicitly
IEnumerable<Animal> fewerAnimals = animals.NotOfType<Animal, Giraffe>();

I think that there are major drawbacks with the style of both of these calls. The first one suffers from a redundant "of type/type of" construct, and the second one just doesn't make sense (do I want a list of animals that are neither Animals nor Giraffes?).

So, is there a way to accomplish what I want? If not, could it be possible in future versions of the language? (I'm thinking that maybe one day we will have named type arguments, or that we only need to explicitly supply type arguments that can't be inferred?)

Or am I just being silly?

like image 975
Christoffer Lette Avatar asked Dec 30 '10 01:12

Christoffer Lette


3 Answers

I am not sure why you don't just say:

animals.Where(x => !(x is Giraffe));

This seems perfectly readable to me. It is certainly more straight-forward to me than animals.NotOfType<Animal, Giraffe>() which would confuse me if I came across it... the first would never confuse me since it is immediately readable.

If you wanted a fluent interface, I suppose you could also do something like this with an extension method predicate on Object:

animals.Where(x => x.NotOfType<Giraffe>())
like image 87
Brian Genisio Avatar answered Nov 04 '22 18:11

Brian Genisio


How about

animals.NotOf(typeof(Giraffe));

Alternatively, you can split the generic parameters across two methods:

animals.NotOf().Type<Giraffe>();

public static NotOfHolder<TSource> NotOf<TSource>(this IEnumerable<TSource> source);

public class NotOfHolder<TSource> : IHideObjectMembers {
    public IEnumerable<TSource> NotOf<TNot>();
}

Also, you need to decide whether to also exclude inherited types.

like image 34
SLaks Avatar answered Nov 04 '22 19:11

SLaks


This might seem like a strange suggestion, but what about an extension method on plain old IEnumerable? This would mirror the signature of OfType<T>, and it would also eliminate the issue of the redundant <T, TExclude> type parameters.

I would also argue that if you have a strongly-typed sequence already, there is very little reason for a special NotOfType<T> method; it seems a lot more potentially useful (in my mind) to exclude a specific type from a sequence of arbitrary type... or let me put it this way: if you're dealing with an IEnumerable<T>, it's trivial to call Where(x => !(x is T)); the usefulness of a method like NotOfType<T> becomes more questionable in this case.

like image 5
Dan Tao Avatar answered Nov 04 '22 19:11

Dan Tao