何时不使用lambdaexpression式

许多问题正在堆栈溢出回答,成员指定如何使用lambdaexpression式解决这些现实世界/时间问题。

我们是否过度使用,是否考虑了使用lambdaexpression式的性能影响?

我发现了一些文章,探讨了lambda与匿名代理vs不同结果for / foreach循环的性能影响

  1. 匿名委托与Lambdaexpression式与函数调用性能
  2. foreach与List.ForEach的性能
  3. .NET / C#循环性能testing(FOR,FOREACH,LINQ和Lambda) 。
  4. DataTable.Select比LINQ更快

在select合适的解决scheme时,评估标准应该是什么? 除了使用lambda时更简洁的代码和可读性的明显原因。

即使我将重点放在第一点上,我首先在整个performance问题上给我2分钱。 除非差别很大或者使用量很大,否则通常我不会在添加微秒的时候对用户造成任何可见的差异。 我强调,在考虑非密集的被调用的方法时,我只是不在乎。 在我devise应用程序的方式上,我有特殊的性能考虑因素。 我关心caching,关于线程的使用,关于调用方法的聪明方法(无论是进行多次调用还是尝试只进行一次调用),是否关联连接等等。实际上,我通常不会不关注原始性能,而是关注可扩展性。 我不关心它是否能够为单个用户提供更好的性能,但是我非常关心能够在不知道影响的情况下为大量同时在线的用户加载系统。

话虽如此,这里我的意见关于第1点。我喜欢匿名方法。 他们给我很大的灵活性和代码优雅。 关于匿名方法的另一个重要特性是它允许我直接使用容器方法中的局部variables(当然,不是从IL的angular度来看,而是从C#的angular度来看)。 他们经常使我省下大量的代码。 我什么时候使用匿名方法? 埃维单次我所需要的那段代码在其他地方是不需要的。 如果它在两个不同的地方使用,我不喜欢复制粘贴作为重用技术,所以我会使用普通的ol代理。 所以,就像shoosh回答,代码重复是不好的。 在理论上,没有性能差异,因为匿名是C#技巧,而不是IL的东西。

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

 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数组,并为每个string调用传递给它的方法。 如果该方法返回true,则会显示“find…”。 您可以通过以下方式调用此方法:

 string[] names = {"Alice", "Bob", "Charles"}; DoSomethingMethod(names, delegate(string p) { return p == "Alice"; }); 

但是,你也可以这样称呼它:

 DoSomethingMethod(names, p => p == "Alice"); 

两者之间的IL没有区别,因为使用Lambdaexpression式的那个更具可读性。 再一次,没有性能影响,因为这些都是C#编译器技巧(不是JIT编译器技巧)。 正如我不觉得我们正在滥用匿名方法,我不觉得我们正在过度使用Lambdaexpression式来表示匿名方法。 当然,同样的逻辑也适用于重复的代码:不要做lambdaexpression式,使用普通的代表。 还有其他的限制可以让你回到匿名方法或纯粹的委托,比如out或者refparameter passing。

关于Lambdaexpression式的其他好处是,完全相同的语法不需要表示匿名方法。 Lambdaexpression式也可以表示……你猜,expression式。 以下面的例子:

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

注意略有不同的签名。 第二个参数接收expression式而不是委托。 调用这个方法的方法是:

 DoSomethingExpression(names, p => p == "Alice"); 

这与使用lambda创build匿名方法时的调用完全相同。 这里的区别是我们不是创build一个匿名方法,而是创build一个expression式树。 这是由于这些expression式树,我们可以将lambdaexpression式转换为SQL,这正是Linq 2 SQL所做的,而不是像where,select等每个子句在引擎中执行的东西。好的东西无论您是创build匿名方法还是发送expression式,调用语法都是相同的。

我的答案不会stream行。

我相信Lambda 99%始终是更好的select有三个原因。

首先,假设你的开发者很聪明,绝对没有错。 其他答案有一个潜在的前提,每个开发者,但你是愚蠢的。 不是这样。

其次,Lamdas(等)是一种现代的语法 – 明天他们会比现在更为平常。 您的项目代码应该来自当前和新兴的惯例。

第三,编写“老式的代码”对你来说可能看起来更容易,但编译器并不容易。 这是非常重要的,遗留方法在编译器修改时几乎没有机会得到改进。 因为编译器随着时间的推移更好地处理它们,所以依靠编译器来扩展它们的Lambdas(et al)可以受益。

总结一下:

  1. 开发人员可以处理它
  2. 每个人都在这样做
  3. 有未来的潜力

再次,我知道这不会是一个受欢迎的答案。 相信我“简单就是最好”也是我的口头禅。 维护是任何来源的重要方面。 我知道了。 但我认为我们用一些老生常谈的经验来掩盖现实。

// 杰瑞

代码重复。
如果你发现自己多次写同一个匿名函数,它不应该是一个。

那么,当我们谈论代表使用的时候,lambda和anonymous方法之间不应该有任何区别 – 它们是相同的,只是语法不同而已。 命名方法(用作委托)也​​与运行时的观点相同。 那么,区别在于使用委托与内联代码之间 – 即

 list.ForEach(s=>s.Foo()); // vs. foreach(var s in list) { s.Foo(); } 

(我希望后者更快)

同样,如果你正在谈论除内存对象之外的其他任何东西,lambda是维护types检查(而不是始终parsingstring)最强大的工具之一。

当然,有些情况下,使用代码的简单foreach将比LINQ版本更快,因为调用的执行次数会减less,并且调用的开销会很小,但是可以测量。 但是,在许多情况下,代码根本就不是瓶颈,简单的代码(特别是对于分组等)比几个纳秒值得多。

还要注意的是,在.NET 4.0中,还有其他Expression节点 ,如循环,逗号等等。语言不支持它们,但运行时却是这样。 我提到这只是为了完整性:我当然不是说你应该使用手动Expressionbuild设,哪里foreach会!

我会说,性能差异通常是如此之小(在循环的情况下,显然,如果你看第二篇文章的结果(顺便说一句,Jon Skeet 在这里有一个类似的文章)),你几乎不应该select除非你正在编写一个软件,其中性能绝对最重要的非function性需求,而且你必须进行微型优化。

何时select什么? 我想这取决于情况,但也取决于人。 只是作为一个例子,一些人认为List.Foreach正常的foreach循环。 我个人比较喜欢后者,因为它通常更具可读性,但是我是谁来反对呢?

经验法则:

  1. 写你的代码是自然的和可读的。
  2. 避免代码重复(lambdaexpression式可能需要一些额外的努力)。
  3. 只有在出现问题时才进行优化,只有用数据来备份实际存在的问题。

任何时候,lambda只是直接将其parameter passing给另一个函数。 不要为函数应用程序创buildlambdaexpression式。

例:

 var coll = new ObservableCollection<int>(); myInts.ForEach(x => coll.Add(x)) 

更好:

 var coll = new ObservableCollection<int>(); myInts.ForEach(coll.Add) 

主要的例外是C#的types推断因为什么原因失败(并且有很多次是真的)。

如果你需要recursion,不要使用lambdas, 否则你会变得非常分心 !

Lambdaexpression式很酷。 在较老的delegate语法,他们有几个优点,他们可以转换为匿名函数或expression式树,从声明推断参数types,他们更干净,更简洁等我看不到真正的价值,不使用lambdaexpression式当你需要一个匿名函数。 早期的风格有一个没有那么大的优势,就是如果不使用它,你可以完全省略参数声明。 喜欢

 Action<int> a = delegate { }; //takes one argument, but no argument specified 

当你必须声明一个空白的委托不做任何事的时候,这是非常有用的,但是这不是一个足够的理由,不能使用lambdas。

Lambdas让你写出快速的匿名方法。 现在,让匿名方法变得毫无意义的地方,即所谓的方法更有意义的地方,lambda毫无意义。 在命名方法中 ,匿名方法可能是不利的(本身不是lambdaexpression式,但是由于这些日子lambdas广泛地代表匿名方法,所以它是相关的):

  1. 因为它倾向于导致逻辑重复(通常是重用是困难的)

  2. 当没有必要写一个,如:

     //this is unnecessary Func<string, int> f = x => int.Parse(x); //this is enough Func<string, int> f = int.Parse; 
  3. 因为编写匿名迭代器块是不可能的。

     Func<IEnumerable<int>> f = () => { yield return 0; }; //impossible 
  4. 因为recursionlambda需要一个更多的quirkiness行,像

     Func<int, int> f = null; f = x => (x <= 1) ? 1 : x * f(x - 1); 
  5. 好吧,因为反思是有点混乱,但这是没有意义的是不是?

除了第3点,其余的不是强烈的理由不使用 lambdas。

另请参阅这个线程 ,了解Func/Action委托的缺点,因为它们经常和lambdaexpression式一起使用。