我如何< T>在LINQ中实现NotOfType,它有一个很好的调用语法?

7uhlpewt  于 2023-05-04  发布在  其他
关注(0)|答案(7)|浏览(182)

我正在尝试为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>();

我认为这两个电话的风格都有很大的缺点。第一个例子有一个多余的“of type/type of”结构,第二个例子没有意义(我想要一个既不是Animals也不是Giraffes的动物列表吗?)).
那么,有没有办法实现我的愿望呢?如果没有,在该语言的未来版本中是否可能?(我在想,也许有一天我们会有命名的类型参数,或者我们只需要显式地提供无法推断的类型参数?)
还是我太傻了

rjjhvcjd

rjjhvcjd1#

我不知道你为什么不直接说:

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

我觉得这是完全可读的。对我来说,它肯定比animals.NotOfType<Animal, Giraffe>()更直接,如果我遇到它,我会感到困惑......第一个永远不会让我感到困惑,因为它是立即可读的。
如果你想要一个流畅的接口,我想你也可以在Object上用一个扩展方法 predicate 来做这样的事情:

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

xnifntxz2#

不如

animals.NotOf(typeof(Giraffe));

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

此外,您还需要决定是否也排除继承类型。

bksxznpy

bksxznpy3#

这似乎是一个奇怪的建议,但是在普通的旧IEnumerable上使用扩展方法怎么样呢?这将镜像OfType<T>的签名,并且还将消除冗余<T, TExclude>类型参数的问题。
我还认为,如果您已经有了一个强类型序列,那么就没有什么理由使用特殊的NotOfType<T>方法;从一个 arbitrary 类型的序列中排除一个特定的类型似乎更有用(在我看来)……或者让我这么说如果处理的是IEnumerable<T>,调用Where(x => !(x is T))是很简单的;在这种情况下,像NotOfType<T>这样的方法的有用性变得更加可疑。

mwg9r5ms

mwg9r5ms4#

我也遇到过类似的问题,在寻找答案的时候遇到了这个问题。
我采用了以下调用语法:

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

它的缺点是枚举集合两次(因此不能用于无限级数或枚举有副作用的集合,不应该重复),但优点是不需要新的帮助函数,含义很清楚。
在我的实际用例中,我还在.OfType<Giraffe>()之后添加了.Where(...)(长颈鹿也包括在内,除非它们满足只对长颈鹿有意义的特定排除条件)

w8ntj3qf

w8ntj3qf5#

如果你要做一个推理的方法,你要一直推理下去。这需要每种类型的示例:

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

qlfbtfca6#

我刚试过这个,它的工作原理...

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

我错过什么了吗?

dffbzjpn

dffbzjpn7#

你可以考虑一下

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

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

相关问题