C# 何时不使用lambda表达式

C# 何时不使用lambda表达式,c#,.net,lambda,anonymous-methods,C#,.net,Lambda,Anonymous Methods,StackOverflow上有许多问题得到了回答,成员们指定了如何使用解决这些现实世界/时间问题 我们是否过度使用了它,是否考虑了使用lambda表达式对性能的影响 我找到了几篇文章,探讨了lambda与匿名委托以及for/foreach循环对性能的影响,结果不同 选择合适的解决方案时,评估标准应该是什么?除了使用lambda时代码更简洁易读的明显原因。代码重复。 如果您发现自己多次编写相同的匿名函数,那么它不应该是一个。好吧,当我们讨论委托用法时,lambda和匿名方法之间不应该有任何

StackOverflow上有许多问题得到了回答,成员们指定了如何使用解决这些现实世界/时间问题

我们是否过度使用了它,是否考虑了使用lambda表达式对性能的影响

我找到了几篇文章,探讨了lambda与匿名委托以及
for
/
foreach
循环对性能的影响,结果不同

  • 选择合适的解决方案时,评估标准应该是什么?除了使用lambda时代码更简洁易读的明显原因。

    代码重复。

    如果您发现自己多次编写相同的匿名函数,那么它不应该是一个。

    好吧,当我们讨论委托用法时,lambda和匿名方法之间不应该有任何区别——它们是相同的,只是语法不同。从运行时的角度来看,命名方法(用作代理)也是相同的。那么,不同之处在于使用委托与内联代码之间的区别——即

    list.ForEach(s=>s.Foo());
    // vs.
    foreach(var s in list) { s.Foo(); }
    
    (我认为后者会更快)

    同样,如果您讨论的不是内存中的对象,lambda是维护类型检查(而不是一直解析字符串)方面最强大的工具之一

    当然,在某些情况下,带有代码的简单
    foreach
    将比LINQ版本更快,因为要执行的调用会更少,并且调用成本很小,但可以测量。然而,在许多情况下,代码并不是瓶颈,更简单的代码(尤其是分组等)的价值远远超过几纳秒


    还要注意的是,在.NET4.0中有for,比如循环、逗号等。该语言不支持它们,但运行时支持。我提到这一点只是为了完整:我当然不是说你应该在
    foreach
    可以使用的地方使用手动
    Expression
    构造

    我要说的是,性能差异通常非常小(在循环的情况下,显然,如果您查看第二篇文章(顺便说一句,Jon Skeet有一篇类似的文章)的结果),您几乎不应该仅出于性能原因而选择解决方案,除非您正在编写一个性能绝对是第一位非功能性需求的软件,而且您真的必须进行微观优化

    什么时候选择什么?我想这取决于情况,也取决于人。举个例子,有些人喜欢List.Foreach而不是普通的Foreach循环。我个人更喜欢后者,因为它通常更具可读性,但我是谁来反对这一点呢?

    经验法则:

  • 编写自然易读的代码
  • 避免代码重复(lambda表达式可能需要额外注意)
  • 仅在出现问题时进行优化,并且仅使用数据来备份问题的实际情况

  • 如果需要递归,不要使用lambdas

    尽管我将重点关注第一点,但我还是会在整个绩效问题上付出2美分。除非差异很大或使用量很大,否则我通常不会为添加时不会对用户造成任何可见差异的微秒而烦恼。我要强调的是,我只在考虑非密集调用方法时才在乎。我真正需要考虑的是应用程序本身的设计。我关心缓存,关心线程的使用,关心调用方法的巧妙方法(是进行多次调用还是尝试只进行一次调用),是否共享连接,等等。事实上,我通常不关注原始性能,而是关注可伸缩性。我不在乎它是否能在一个用户身上运行得更好,但我很在乎它是否能在没有注意到影响的情况下,在系统中同时加载大量用户

    话虽如此,我对第一点的看法是这样的。我喜欢匿名方法。它们给了我极大的灵活性和代码优雅。匿名方法的另一个重要特性是,它们允许我直接使用容器方法中的局部变量(当然是从C#的角度,而不是从IL的角度)。他们经常给我一大堆代码。何时使用匿名方法?每一次,我需要的代码片段在其他地方都是不需要的。如果它在两个不同的地方使用,我不喜欢复制粘贴作为一种重用技术,所以我将使用普通的ol'委托。所以,正如shoosh所回答的,代码重复是不好的。从理论上讲,由于匿名是C#技巧,而不是IL东西,因此没有性能差异

    我对匿名方法的大部分看法都适用于lambda表达式,因为后者可以用作表示匿名方法的紧凑语法。让我们假设以下方法:

    public static void DoSomethingMethod(string[] names, Func<string, bool> myExpression)
    {
        Console.WriteLine("Lambda used to represent an anonymous method");
        foreach (var item in names)
        {
            if (myExpression(item))
                Console.WriteLine("Found {0}", item);
        }
    }
    
    但是,你也可以用以下方式来称呼它:

    string[] names = {"Alice", "Bob", "Charles"};
    DoSomethingMethod(names, delegate(string p) { return p == "Alice"; });
    
    DoSomethingMethod(names, p => p == "Alice");
    
    两者之间的IL没有区别,因为使用Lambda表达式的IL更具可读性。同样,这对性能没有影响,因为这些都是C#编译器技巧(不是JIT编译器技巧)。正如我不觉得我们过度使用匿名方法一样,我也不觉得我们过度使用Lambda表达式来表示匿名方法。当然,同样的逻辑也适用于重复代码:不要使用lambdas,而是使用常规委托。还有其他一些限制将您引导回匿名方法或普通委托,比如out或ref参数传递

    Lambda表达式的另一个优点是,完全相同的语法不需要表示匿名方法。Lambda表达式也可以表示。。。你猜对了,表情。以以下为例:

    public static void DoSomethingExpression(string[] names, System.Linq.Expressions.Expression<Func<string, bool>> myExpression)
    {
        Console.WriteLine("Lambda used to represent an expression");
        BinaryExpression bExpr = myExpression.Body as BinaryExpression;
        if (bExpr == null)
            return;
        Console.WriteLine("It is a binary expression");
        Console.WriteLine("The node type is {0}", bExpr.NodeType.ToString());
        Console.WriteLine("The left side is {0}", bExpr.Left.NodeType.ToString());
        Console.WriteLine("The right side is {0}", bExpr.Right.NodeType.ToString());
        if (bExpr.Right.NodeType == ExpressionType.Constant)
        {
            ConstantExpression right = (ConstantExpression)bExpr.Right;
            Console.WriteLine("The value of the right side is {0}", right.Value.ToString());
        }
     }
    
    这与我们使用lambda创建匿名方法时进行的调用完全相同。这里的区别在于,我们不是创建匿名方法,而是创建表达式树。它是
    DoSomethingExpression(names, p => p == "Alice");
    
    var coll = new ObservableCollection<int>();
    myInts.ForEach(x => coll.Add(x))
    
    var coll = new ObservableCollection<int>();
    myInts.ForEach(coll.Add)
    
    Action<int> a = delegate { }; //takes one argument, but no argument specified
    
    //this is unnecessary 
    Func<string, int> f = x => int.Parse(x);
    
    //this is enough
    Func<string, int> f = int.Parse;
    
    Func<IEnumerable<int>> f = () => { yield return 0; }; //impossible
    
    Func<int, int> f = null;
    f = x => (x <= 1) ? 1 : x * f(x - 1);