【问题标题】:How can I implement NotOfType<T> in LINQ that has a nice calling syntax?如何在具有良好调用语法的 LINQ 中实现 NotOfType<T>?
【发布时间】:2011-06-01 08:23:39
【问题描述】:

我正在尝试为NotOfType 提供一个实现,它具有可读的调用语法。 NotOfType 应该是 OfType&lt;T&gt; 的补充,因此会产生所有 类型为 T

的元素

我的目标是实现一个可以像OfType&lt;T&gt; 一样调用的方法,就像这个 sn-p 的最后一行:

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

我认为这两种调用的风格都有很大的缺陷。第一个具有冗余的“类型/类型”构造,而第二个则没有意义(我想要一个既不是动物也不是长颈鹿的动物列表?)。

那么,有没有办法实现我想要的?如果没有,在该语言的未来版本中是否有可能? (我在想也许有一天我们会命名类型参数,或者我们只需要显式提供无法推断的类型参数?)

还是我傻了?

【问题讨论】:

  • 如果我觉得这样的框架/共享代码会得到相当多的重用,我只会编写这样的框架/共享代码。您多久需要一次这样的方法,它是否证明成本(考虑设计困难所花费的时间)是合理的?
  • 你的两个版本做的事情大不相同。
  • @Lette:第一个也会排除继承的类型。
  • @Merlyn:我下班了,所以没有实际成本。但是,是的,这里可能找不到太多的重用。暂时把它当作一个实验。
  • @SLaks:不错的收获。我没有用比两个级别更深的继承层次结构测试它,但现在我会的。谢谢。但是,这对我的问题的本质没有任何影响,这更多是关于方法签名而不是实现。

标签: c# linq type-inference code-readability


【解决方案1】:

怎么样

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

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

【讨论】:

  • 我写了一篇博文详细解释:blog.slaks.net/2010/12/partial-type-inference-in-net.html
  • 最后,我得出的结论是,您的解决方案最适合我的具体情况。我考虑到代码将用于自动化验收测试这一事实,并且它们有时可能会被可能不是全职开发人员的人阅读。拥有一个强类型的调用将更容易保持后续代码更具(人类)可读性。谢谢。
【解决方案2】:

这似乎是一个奇怪的建议,但是对于普通的旧 IEnumerable 的扩展方法呢?这将反映OfType&lt;T&gt; 的签名,也将消除冗余&lt;T, TExclude&gt; 类型参数的问题。

我还认为,如果您已经有一个强类型序列,那么没有什么理由需要一个特殊的NotOfType&lt;T&gt; 方法;从 arbitrary 类型的序列中排除特定类型似乎更有用(在我看来)......或者让我这样说:如果你正在处理 @987654325 @,调用Where(x =&gt; !(x is T))是微不足道的;在这种情况下,NotOfType&lt;T&gt; 这样的方法的实用性变得更加可疑。

【讨论】:

  • 关于非泛型 IEnumerable 的有趣想法。它将使调用代码看起来完全符合我的要求。显然,它不会返回强类型序列,这是一个小不便。
  • 关于第 2 段: 有人可以认为NotOfType&lt;T&gt; 的有用性与OfType&lt;T&gt; 相匹配...(有趣的是尝试将您的论点应用于OfType&lt;T&gt;。)我的论点仅与代码可读性有关。仅供参考,在实际的业务案例中,我有一系列 Event 元素,它们属于不同的子类型,在一个特定场景中,要求要求列出所有事件,除了一种特定类型。这个实验就是要找到一种方法来用易于阅读的代码表达这个要求。
  • @Lette: OfType&lt;T&gt;() 不同,因为它改变了序列的类型。 NotOfType 无法更改序列的类型。
  • @SLaks:你们俩都完全正确。我现在有点尴尬,因为我完全错过了 OfType 方法的源是 IEnumerable 而不是 IEnumerable...不过,这仍然是一个很好的讨论。
【解决方案3】:

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

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

这对我来说似乎完全可读。它对我来说肯定比animals.NotOfType&lt;Animal, Giraffe&gt;() 更直接,如果我遇到它会让我感到困惑......第一个永远不会让我感到困惑,因为它可以立即阅读。

如果您想要一个流畅的界面,我想您也可以使用Object 上的扩展方法谓词来做类似的事情:

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

【讨论】:

    【解决方案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));
      

      我错过了什么吗?

      【讨论】:

      • 并不是说它不工作。我的反对意见是,您需要为通用调用指定两种类型(TSourceTResult),而对于这个特定调用,这只是看起来很奇怪。
      【解决方案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>());
        

        它的缺点是枚举了两次集合(因此不能与无限系列一起使用),但优点是不需要新的辅助函数,含义清晰。

        在我的实际用例中,我最终还在.OfType&lt;Giraffe&gt;() 之后添加了.Where(...)(长颈鹿也包括在内,除非它们满足仅对长颈鹿有意义的特定排除条件)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-06-24
          • 2012-09-08
          • 1970-01-01
          • 2019-07-11
          • 2019-11-14
          • 2010-10-07
          相关资源
          最近更新 更多