【问题标题】:Apply function to all elements of collection through LINQ [duplicate]通过LINQ将函数应用于集合的所有元素[重复]
【发布时间】:2010-10-23 20:07:01
【问题描述】:

我最近开始使用 LINQ,它令人惊叹。我想知道 LINQ 是否允许我在不使用 foreach 的情况下将函数(任何函数)应用于集合的所有元素。类似于 python lambda 函数的东西。

例如,如果我有一个 int 列表,我可以使用 LINQ 为每个元素添加一个常量

如果我有一个 DB 表,我可以使用 LINQ 为所有记录设置一个字段吗?

我正在使用 C#

【问题讨论】:

    标签: c# .net linq


    【解决方案1】:

    使用 LINQ 执行此操作的惯用方法是处理集合并返回以您想要的方式映射的新集合。例如,要为每个元素添加一个常量,您需要类似

    var newNumbers = oldNumbers.Select(i => i + 8);
    

    以一种功能性的方式执行此操作,而不是频繁地改变现有集合的状态,这有助于您以一种更易于阅读且更易于编译器推理的方式分离不同的操作。

    如果您确实想要对集合的每个元素应用一个操作(一个具有与集合的实际内容无关的副作用的操作),那么 LINQ 并不是最适合的,尽管您可以使用 Select 伪造它(或编写自己的 IEnumerable 扩展方法,正如许多人所做的那样。)在这种情况下,最好坚持使用 foreach 循环。

    【讨论】:

    • 更不用说在迭代集合时对其进行变异是危险的,甚至是不可能的,具体取决于语言。
    • 在 C# 中,改变集合的元素是公平的游戏,但添加到集合中或从集合中删除可能会失败或产生不需要的结果。
    • 使用 Select 方法只对某些类型有帮助,比如 int。如果您想将列表中对象的属性设置为某个值怎么办?使用 .ForEach() 不是更简单、更具可扩展性吗?
    • 如果您已经知道新值,那么通常(本着 LINQ 的精神)您会倾向于创建一个包含适当值的新列表,而不是更改旧列表。当然,如果你确实有一个列表并且你真的想更改那个特定的列表——也许它是数据绑定的——那么 .ForEach() 也可以。
    • LINQ 延迟执行可能是Select 的问题,如果您稍后不对结果进行迭代。
    【解决方案2】:

    哈哈,伙计,我几个小时前才问过这个问题(有点)......试试这个:

    示例:

    someIntList.ForEach(i=>i+5);
    

    ForEach() 是 .NET 内置方法之一

    这将修改列表,而不是返回一个新列表。

    【讨论】:

    • 请注意,这仅适用于实际的 List 集合,除非您已定义自己的 ForEach 方法来执行此操作。
    • 我相信 mquander 指的是 ForEach 没有在 IEnumerable 上定义,只是 List
    • 是的,它在大多数情况下都不起作用,因为通常你会从链接查询中得到一个 IEnumerable
    • 我发现大部分时间添加.ToList().ForEach(...) 就是我所需要的。
    【解决方案3】:

    解决此问题的常用方法是在IEnumerable<T> 上添加您自己的ForEach 泛型方法。这是我们在MoreLINQ 中得到的:

    public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
    {
        source.ThrowIfNull("source");
        action.ThrowIfNull("action");
        foreach (T element in source)
        {
            action(element);
        }
    }
    

    (其中ThrowIfNull 是任何引用类型的扩展方法,这很明显。)

    看看这是否是 .NET 4.0 的一部分会很有趣。它与 LINQ 的函数式风格背道而驰,但毫无疑问,很多人觉得它很有用。

    一旦你有了它,你就可以写出这样的东西:

    people.Where(person => person.Age < 21)
          .ForEach(person => person.EjectFromBar());
    

    【讨论】:

    • 我从来没有真正理解人们必须这样做的冲动。 foreach 循环对我来说更具可读性;将获取数据的“功能”部分和对其进行操作的“动作”部分分开有助于阐明算法的结构。
    • 是的,我自己通常更喜欢 foreach 循环。另一方面,如果您已经看到了一个应用于每个元素的委托,我不确定在显式 foreach 循环中调用该委托是否更具可读性。我不会经常使用它,但偶尔它似乎是一个不错的解决方案。
    • @Lodewijk:以什么方式使用必须具有副作用才能发挥作用的方法? Where 部分是功能性的...... ForEach 部分不是那么多,IMO。
    • 进行就地更改而不是返回新类型绝对是可怕的。这会让大量程序员感到困惑。好处减少到不必嵌套 foreaches 或以其他方式编写丑陋的代码。 Oneliners 的可读性很强。
    • @Alex,可能不是。你的意思是?这些“功能性”功能是扩展控制流的方法,“地图”特别受到很多人的喜爱。它可以轻松获取集合并将函数应用于其中的所有内容。拥有“地图”而不是 4 行 foreach 很好,因为它显示了它所做的事情不那么含糊(想想零或一索引头痛),而且更简短。让 map 返回一个新集合而不是进行内联更改是更标准和更通用的。不过,这个“ForEach”并不是一个 map 函数,与常规的 foreach 循环相比优势较小。
    【解决方案4】:

    你可以试试

    var foo = (from fooItems in context.footable select fooItems.fooID + 1);
    

    返回 id 的 +1 列表,您可以对 select 子句中的任何内容使用函数来执行相同操作。

    更新:正如 Jon Skeet 所建议的,这是我刚刚发布的代码 sn-p 的更好版本:

    var foo = context.footable.Select(foo => foo.fooID + 1);
    

    【讨论】:

    • 我建议在您只需要进行简单投影的情况下,查询表达式位于顶部。我会使用“var foo = context.footable.Select(foo => foo.fooID + 1);”
    【解决方案5】:

    你也可以考虑并行,特别是如果你不关心顺序,更关心为每个项目完成某事:

    SomeIEnumerable<T>.AsParallel().ForAll( Action<T> / Delegate / Lambda )
    

    例如:

    var numbers = new[] { 1, 2, 3, 4, 5 };
    numbers.AsParallel().ForAll( Console.WriteLine );
    

    HTH。

    【讨论】:

    • 请小心 AsParallel().ForAll(),因为它会导致不可预测的结果。例如,我有一个按钮可以在单击时执行此代码: myEnumerable.AsParallel().ForAll(i as string => otherDictionary.Add(i, 0)) 。它会将 null 作为 key 添加到 otherDictionary。我不得不重写以使用 foreach 循环。很奇怪。
    • @YukiSakura 并不是 AsParallel().ForAll() 的错 - 危险的位是在多个线程之间使用共享声明,然后“i as string”的转换是可能的原因传入一个空键。愿意分享更完整的代码示例,以获取有关如何改进的反馈?
    【解决方案6】:

    我找到了一些在字典中执行的方法,其中包含我的自定义类方法

    foreach (var item in this.Values.Where(p => p.IsActive == false))
                item.Refresh();
    

    “this”源自:Dictionary

    class MyCustomClass 
    {
       public void Refresh(){}
    }
    

    【讨论】:

      【解决方案7】:

      对于不支持ForEach 的集合,您可以在Parallel 类中使用静态ForEach 方法:

      var options = new ParallelOptions() { MaxDegreeOfParallelism = 1 };
      Parallel.ForEach(_your_collection_, options, x => x._Your_Method_());
      

      【讨论】:

      • 这真的很不直观。
      【解决方案8】:

      或者你可以破解它。

      Items.All(p => { p.IsAwesome = true; return true; });
      

      【讨论】:

      • 这是在破解“All”以将其变成“ForEach”。我有一种不好的感觉,这会误导一些新手认为“全部”意味着“在所有元素上执行”。
      • 有趣。这实际上会编辑基础集合吗?对象是否返回副本?
      • @Zimano 它确实编辑了基础集合,但不返回副本。
      猜你喜欢
      • 1970-01-01
      • 2023-01-24
      • 1970-01-01
      • 1970-01-01
      • 2017-03-09
      • 2014-11-15
      • 2020-02-04
      • 1970-01-01
      • 2016-05-02
      相关资源
      最近更新 更多