使用匿名方法有任何开销吗?

时间:2012-02-22 12:41:48

标签: c# anonymous-methods

我想知道在创建后台工作程序时是否因使用匿名方法而产生任何开销。

例如:

public void SomeMethod()
{
    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += (sender, e) =>
    {
        //large amount of code
    }

    worker.RunWorkerAsync();
}

上面的示例是否比在单独的方法中定义//large amount of code更好或更差?

在线定义后台工作程序方法是否有任何开销,特别是在经常调用SomeMethod()的情况下?

8 个答案:

答案 0 :(得分:5)

从它们创建委托时,如何处理命名方法和无序方法有一点不同。

匿名方法的委托被缓存,因此检查委托是否已存在于缓存中的开销很小。另一方面,如果您多次运行该方法,它将重用缓存的委托,而不是创建新的委托。

命名方法的委托不会被缓存,因此每次都会创建。

除此之外没有区别。 anonumous方法将在编译时创建,并且像常规方法一样存在于代码中,只有编译器知道的名称。

答案 1 :(得分:2)

首先,您可能不应该将大量代码放入匿名方法中。如果为这个方法创建一个单独的方法,甚至更好的方法,那么它将更具可读性。

对于生成的IL,如果lambda没有关闭任何变量,那么生成的IL代码与将代码放在普通命名方法中的情况相同(除了生成的方法具有不可说的名称)。

另一方面,如果你关闭某个变量,编译器会创建一个闭包类来保存该字段中的变量。对于本地变量访问,字段访问稍微贵一些。

总而言之,如果您关闭某些变量,则开销很小(包括需要进行垃圾回收的更多对象)。在大多数情况下,这并不重要,担心这将是过早的优化。但如果你认为它确实很重要,你应该对代码进行分析。

答案 2 :(得分:1)

每当匿名方法(包括lambdas)关闭变量时,编译器就会创建一个类来为您保存这些变量。每当创建委托时,该类的新实例也是如此。这显然为运行时添加了额外的工作,但在大多数情况下通常可以忽略不计。

答案 3 :(得分:0)

前几天我正在测试它(通过使用StopWatch类)。据我所知,在直接调用方法之间没有明显的性能差异......

SomeMethod();

......或通过匿名方法......

() => SomeMethod();

答案 4 :(得分:0)

它主要影响可读性 - 一个地方的大量代码几乎从来都不好; - )

就绩效而言,请参阅When is optimization premature?

答案 5 :(得分:0)

这是反编译员所说的:

[CompilerGenerated]
private static DoWorkEventHandler CS$<>9__CachedAnonymousMethodDelegate1;

[CompilerGenerated]
private static void <SomeMethod1>b__0(object sender, DoWorkEventArgs e)
{
    throw new NotImplementedException();
}

public void SomeMethod1()
{
    BackgroundWorker worker = new BackgroundWorker();
    BackgroundWorker backgroundWorker = worker;
    backgroundWorker.DoWork += (object sender, DoWorkEventArgs e) => throw new NotImplementedException();
    worker.RunWorkerAsync();
}

public void SomeMethod2()
{
    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += worker_DoWork;
    worker.RunWorkerAsync();
}

private void worker_DoWork(object sender, DoWorkEventArgs e)
{
    throw new NotImplementedException();
} 

编辑:

查看IL代码,首次创建/分配方法委派的开销很小。

答案 6 :(得分:0)

答案 7 :(得分:0)

我一直关心的是-循环使用委托。事实证明,即使在循环中使用时,编译器也足够聪明,可以缓存匿名函数。

这是我的代码(1000万次迭代):

function getColumnHeight(col,sh,ss){
  var ss=ss || SpreadsheetApp.getActive();
  var sh=sh || ss.getActiveSheet();
  var col=col || sh.getActiveCell().getColumn();
  var rg=sh.getRange(1,col,sh.getLastRow(),1);
  var vA=rg.getValues();
  while(vA[vA.length-1][0].length==0){
    vA.splice(vA.length-1,1);
  }
  return vA.length;
}

结果:

第一时间:151毫秒

2nd:148毫秒

第二次运行

第一时间:149毫秒

2nd:175毫秒(这次变慢了……谁知道)