Как я могу реализовать NotOfType <T>в LINQ, который имеет хороший синтаксис вызова? - PullRequest
22 голосов
/ 30 декабря 2010

Я пытаюсь найти реализацию для NotOfType, которая имеет читаемый синтаксис вызова.NotOfType должен быть дополнением к OfType<T> и, следовательно, будет давать все элементы, которые не типа T

Моя цель состояла в том, чтобы реализовать метод, который будет вызываться так жеOfType<T>, как в последней строке этого фрагмента:

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>();

Однако я не могу придумать реализацию, которая поддерживает этот специфический синтаксис вызова.

Это то, что я 'мы уже пробовали:

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));
    }
}

Вызов этих методов будет выглядеть следующим образом:

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

и

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

Я думаю, что есть серьезные недостатки сстиль обоих этих звонков.Первый страдает от избыточной конструкции типа / типа, а второй просто не имеет смысла (хочу ли я список животных, которые не являются ни животными, ни жирафами?).

ИтакЕсть ли способ выполнить то, что я хочу?Если нет, возможно ли это в будущих версиях языка?(Я думаю, что, возможно, однажды у нас будут именованные аргументы типа, или что нам нужно только явно указать аргументы типа, которые не могут быть выведены?)

Или я просто глупый?

Ответы [ 7 ]

27 голосов
/ 30 декабря 2010

Я не уверен, почему вы просто не говорите:

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

Это кажется мне совершенно понятным.Это, безусловно, более прямолинейно для меня, чем animals.NotOfType<Animal, Giraffe>(), что могло бы сбить меня с толку, если бы я натолкнулся на него ... первое никогда не смутило бы меня, поскольку оно сразу читаемо.

Если вы хотели бы иметь свободный интерфейс,Я полагаю, вы также можете сделать что-то подобное с предикатом метода расширения для Object:

animals.Where(x => x.NotOfType<Giraffe>())
11 голосов
/ 30 декабря 2010

Как насчет

animals.NotOf(typeof(Giraffe));

В качестве альтернативы, вы можете разделить общие параметры на два метода :

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

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

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

Также вам необходимо решитьследует ли также исключать унаследованные типы.

5 голосов
/ 30 декабря 2010

Это может показаться странным предложением, но как насчет метода расширения на обычном старом IEnumerable?Это отражало бы сигнатуру OfType<T>, а также устраняло бы проблему избыточных параметров типа <T, TExclude>.

Я бы также сказал, что если у вас уже есть строго типизированная последовательность, оченьнебольшая причина для особого NotOfType<T> метода;Мне кажется, гораздо более полезно (на мой взгляд) исключить конкретный тип из последовательности произвольного типа ... или позвольте мне выразиться так: если вы имеете дело с IEnumerable<T>звонить Where(x => !(x is T)) тривиально;полезность такого метода, как NotOfType<T>, в этом случае становится более сомнительной.

1 голос
/ 30 декабря 2010

Если вы собираетесь создать метод для вывода, вы хотите сделать вывод полностью. Для этого требуется пример каждого типа:

public static class ExtMethods
{
    public static IEnumerable<T> NotOfType<T, U>(this IEnumerable<T> source)
    {
        return source.Where(t => !(t is U));
    }
      // helper method for type inference by example
    public static IEnumerable<T> NotOfSameType<T, U>(
      this IEnumerable<T> source,
      U example)
    {
        return source.NotOfType<T, U>();
    }
}

вызывается

List<ValueType> items = new List<ValueType>() { 1, 1.0m, 1.0 };
IEnumerable<ValueType> result = items.NotOfSameType(2);
0 голосов
/ 28 февраля 2017

У меня была похожая проблема, и я наткнулся на этот вопрос, когда искал ответ.

Вместо этого я согласился на следующий синтаксис вызова:

var fewerAnimals = animals.Except(animals.OfType<Giraffe>());

Недостатком является то, что он перечисляет коллекцию дважды (поэтому его нельзя использовать с бесконечной серией), но преимущество в том, что не требуется никакой новой вспомогательной функции, и смысл понятен.

В моем реальном случае использования я также добавил .Where(...) после .OfType<Giraffe>() (жирафы также включены, если они не удовлетворяют определенному условию исключения, которое имеет смысл только для жирафов)

0 голосов
/ 30 июня 2016

Вы могли бы рассмотреть это

public static IEnumerable NotOfType<TResult>(this IEnumerable source)
{
    Type type = typeof(Type);

    foreach (var item in source)
    {
       if (type != item.GetType())
        {
            yield return item;
        }
    }
}
0 голосов
/ 26 мая 2016

Я только что попробовал это, и это работает ...

public static IEnumerable<TResult> NotOfType<TExclude, TResult>(this IEnumerable<TResult> sequence)
    => sequence.Where(x => !(x is TExclude));

Я что-то упустил?

...