【问题标题】:How To Create An Extension Method In C# Using A Predicate如何在 C# 中使用谓词创建扩展方法
【发布时间】:2011-06-22 17:48:50
【问题描述】:

我正在尝试创建一个名为 RemoveWhere 的扩展方法,该方法根据谓词从 List 集合中删除一个项目。例如

var 结果 = products.RemoveWhere(p => p.ID == 5);

我使用 Microsoft 的 Where 扩展方法签名作为起点。到目前为止,这是我所拥有的:

public static List<T> RemoveWhere<T>(this List<T> source, Func<T, List<T>> predicate)
{
    if (source == null)
    {
        throw new ArgumentNullException("source", "The sequence is null and contains no elements.");
    }

    if (predicate == null)
    {
        throw new ArgumentNullException("predicate", "The predicate function is null and cannot be executed.");
    }

    // how to use predicate here???

}

我不知道如何使用谓词。有人可以帮我完成这个吗?谢谢!

【问题讨论】:

  • 让我们从批评你目前所拥有的开始。为什么该方法返回一个列表?它是否返回一个 new 列表并删除了项目,并保持源列表不变?还是从源列表中删除项目?如果是前者,那么您错误地说明了您的方法的作用。如果是后者,那么返回值的含义是什么?为什么要退货?
  • 接下来,为什么谓词取一个项目并返回一个项目列表?根据定义,谓词是一个返回bool的函数。
  • 错了,你可以改用System.Predicate 类型。
  • 使用列表的 RemoveAll 方法。它需要一个谓词作为参数。请参阅下面的答案
  • 第三,你为什么要这样做?如果这是一个学习练习,那就太好了——弄清楚如何编写已经存在的功能是了解该功能的绝佳方式。但是,如果您是为了实际工作而这样做,为什么不使用 List.RemoveAll?这似乎完全符合您的要求。

标签: c# visual-studio linq extension-methods


【解决方案1】:

谓词参数应该是:Func&lt;T,bool&gt;

public static List<T> RemoveWhere<T>(this List<T> source, Func<T, bool > predicate)
{
    if (source == null)
    {
        throw new ArgumentNullException("source", "The sequence is null and contains no elements.");
    }

    if (predicate == null)
    {
        throw new ArgumentNullException("predicate", "The predicate function is null and cannot be executed.");
    }

    // how to use predicate here???
    var result = new List<T>();
    foreach(var item in source)
    {
        if(!predicate(item))
        {
            result.Add(item);
        }
    }

    return result;
}

编辑:正如其他人指出的那样,此方法要么名称错误,要么已存在于 List 中。我的猜测只是您试图了解方法本身如何使用传入的委托。为此,您可以查看我的示例。如果那不是您的意图,我将删除此答案,因为代码确实毫无意义。

【讨论】:

  • 这也不会延迟执行,这可能不是调用者所期望的。
  • 好吧,如果他期待一个具体的 List 返回,你会如何推迟执行?
  • 我不知道谓词返回布尔结果。那和你的代码对我帮助很大!谢谢!
  • @BFree 我可以询问谓词条件的 (!NOT) 背后的推理吗?它是否不需要是真的才能成为有效的可操作项目?
  • @BFree 没关系,我一路上忘记了方法的上下文;)
【解决方案2】:

列表中已经有一个方法可以尝试。 Predicate 应该是 Predicate 然后你可以使用 source.RemoveAll(predicate)

【讨论】:

    【解决方案3】:

    正如其他人指出的那样,List&lt;T&gt;.RemoveAll 会做你想做的事。但是,如果这是一种学习体验,或者您想对任何 IList&lt;T&gt;(没有 RemoveAll)进行操作,则应该与 RemoveAll 相同,但作为一种扩展方法。

    public static void RemoveWhere<T>(this IList<T> source, Func<T, bool> predicate)
    {
      //exceptions here...
    
      // how to use predicate here???
      for(int c = source.Count-1 ; c >= 0 ; c--)
      {
        if(predicate(source[c]))
        {
          source.RemoveAt(c);
        }
      }
    }
    

    【讨论】:

    • 这个和其他代码答案之间的区别在于,这个方法改变了原始的IList&lt;T&gt;,就像RemoveAll一样,而其他代码没有,而是返回一个新的List&lt;T&gt;。采取的方法取决于目标。
    【解决方案4】:

    正如 boca 所观察到的,List&lt;T&gt; 已经有办法做到这一点。但是,一个更大的问题是,这确实不是您应该创建新扩展方法的场景。已经有一个采用谓词的扩展方法:Where

    当然,这样做:

    var result = list.Where(x => x != 5).ToList();
    

    比使用RemoveAll的代码多一点:

    list.RemoveAll(x => x == 5);
    

    但是:

    • 它还会构建一个新列表,而不是就地修改现有列表,
    • list 实际上可以是任何IEnumerable&lt;T&gt;,而不仅仅是List&lt;T&gt;
    • Where 方法是一种常用的、有据可查的扩展方法,任何熟练的 C# 程序员都能一眼认出它
    • 任何阅读该代码的人都清楚它正在创建一个新列表,并且
    • 如果您不想想要创建一个新列表,只需离开ToList() 并枚举result

    我真的很难想象我想为IEnumerable&lt;T&gt; 编写一个带有谓词的扩展方法。不使用Where() 可以节省很少的钱。

    【讨论】:

      猜你喜欢
      • 2016-02-05
      • 2016-09-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多