【问题标题】:Closures and Lambda in C#C# 中的闭包和 Lambda
【发布时间】:2009-08-20 15:24:11
【问题描述】:

我了解了闭包和 lambda 表达式的基本原理,但我正在努力思考幕后发生的事情,以及何时在我的代码中使用它们是/不切实际的。考虑以下示例,它采用名称集合并返回以字母 C 开头的任何名称...

    static void Main(string[] args)
    {
        List<string> names = new List<string>();
        names.AddRange(new string[]
        {
            "Alan", "Bob", "Chris", "Dave", "Edgar", "Frank"
        });


        names.FindAll(x => x.StartsWith("C")).ForEach(
            i => Console.WriteLine(i));

    }

首先,有没有更直接的方法可以写出这个表达式?其次,“FindAll”不是要为保存匹配项的新集合分配内存吗?我肯定看到语法更优雅,但我想确保以后在处理更大的集合时不会遇到性能问题。编译器是否在幕后做了一些优化巫术使我的担忧无效?

【问题讨论】:

    标签: c# lambda closures


    【解决方案1】:

    是的,FindAll 将创建一个新列表。您想要“Where”,它将返回一个 IEnumerable 对象,该对象知道如何遍历现有列表:

    foreach (string name in names.Where(n => n.StartsWith("C") ) ) 
    {
        Console.WriteLine(name);
    }
    

    但该代码中没有闭包,因为没有要捕获的局部变量。

    【讨论】:

    • 好的,所以我可能还不太了解闭包的基础知识。尽管如此,我在这里得到的所有答案都很棒,让我走得更远......谢谢大家。
    • 这向我解释了闭包和 lambda 之间的区别:“代码中没有闭包,因为没有要捕获的局部变量。”
    【解决方案2】:

    说使用“Where”的其他答案是正确的。补充一点:您还可以使用查询理解语法使“Where”看起来更好:

       var query = from name in names where name.StartsWith("C") select name;
       foreach(var result in query) Console.WriteLine(result);
    

    请注意,出于文体方面的考虑,我建议表达式没有副作用,而语句总是有副作用。因此,我个人会使用 foreach statement 而不是 ForEach subexpression 来执行输出副作用。很多人不同意这一点,但我认为它使代码更清晰。

    【讨论】:

      【解决方案3】:

      您应该使用Where 而不是FindAllWhere 将为您的条件迭代集合并允许您执行您的操作,而不是创建一个满足您的条件的新集合,然后迭代该集合并执行您的操作。

      【讨论】:

        【解决方案4】:

        您是对的,使用List&lt;T&gt;.FindAll 方法将创建并返回一个新的List&lt;T&gt;

        如果您能够使用 LINQ,那么有很多方法可以一次将其结果流式传输一个项目,而不是返回一个完全填充的集合:

        foreach (var i in names.Where(x => x.StartsWith("C")))
        {
            Console.WriteLine(i);
        }
        

        没有作用于IEnumerable&lt;T&gt; 的内置ForEach 方法,但如果您真的需要该功能,编写自己的扩展是微不足道的:

        names.Where(x => x.StartsWith("C")).ForEach(Console.WriteLine);
        
        // ...
        
        public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
        {
            foreach (T item in source)
            {
                action(item);
            }
        }
        

        【讨论】:

        • 这正是我打算做的。我还提到可以简化列表创建:List&lt;string&gt; names = new List&lt;string&gt;{"Alan", "Bob", "Chris", "Dave", "Edgar", "Frank"};
        【解决方案5】:

        是什么让一个表达式特别成为一个闭包是词法作用域,不是吗?

        字符串前缀 = "C";
        // 范围内包含的前缀值
        names.FindAll(x => x.StartsWith(prefix)).ForEach(...);
        

        甚至

        Func 过滤器 = null;
        
        {
            字符串前缀 = "C";
            // 范围内包含的前缀值
            filter = x => x.StartsWith (前缀);
        }
        
        // 查找所有以“C”开头的名称
        names.FindAll (filter).ForEach (...);
        

        还是我遗漏了什么或做出了无根据的假设?

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-09-01
          • 2023-04-01
          • 2012-03-24
          • 1970-01-01
          • 2016-06-16
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多