Подтвердить что ты не робот

Как я могу реализовать NotOfType <T> в LINQ, который имеет хороший синтаксис вызова?

Я пытаюсь придумать реализацию для 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>();

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

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

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

4b9b3361

Ответ 1

Как насчет

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

Кроме того, вам нужно решить, следует ли также исключать унаследованные типы.

Ответ 2

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

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

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

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

animals.Where(x => x.NotOfType<Giraffe>())

Ответ 3

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

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

Ответ 4

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

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

Ответ 5

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

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

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

Ответ 6

Вы можете подумать об этом

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

    foreach (var item in source)
    {
       if (type != item.GetType())
        {
            yield return item;
        }
    }
}

Ответ 7

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

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

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

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

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