Как я могу реализовать 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>();
Я думаю, что есть серьезные недостатки в стиле обоих этих вызовов. Первая из них имеет избыточную конструкцию типа/типа, а вторая просто не имеет смысла (хочу ли я список животных, которые не являются ни Животные, ни Жирафы?).
Итак, есть ли способ выполнить то, что я хочу? Если нет, возможно ли это в будущих версиях языка? (Я думаю, что, возможно, однажды мы будем иметь аргументы типа типа или нам нужно явно указывать аргументы типа, которые нельзя вывести?)
Или я просто глуп?
Ответы
Ответ 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>()
(жирафы также включены, если они не соответствуют определенному условию исключения, которое имеет смысл только для жирафов)