2012-02-22 30 views
5

我想知道在创建后台工作时是否有任何通过使用匿名方法产生的开销。使用匿名方法是否存在任何开销?

例如:

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

    worker.RunWorkerAsync(); 
} 

会上面的例子是任何比在单独的方法限定​​更好或更差?

在定义后台工作器方法时是否会产生任何开销,特别是如果经常调用SomeMethod()

+0

如果有大量的代码,你可能想重构它到少量的代码调用其他方法... – Chris 2012-02-22 12:48:40

回答

4

有一个小的区别在从他们创建委托时如何处理命名方法和匿名方法。

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

命名方法的委托没有被缓存,所以每次都会被创建。

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

1

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

+0

所以我认为,如果没有闭包,它不会创建类,所以它不会有什么区别? – Chris 2012-02-22 12:50:40

+0

@Chris是的,编译器足够聪明,只有在需要时才能创建一个匿名类型(保持匿名方法加上变量关闭)。 – 2012-02-22 12:53:38

+0

创建委托时,不会创建闭包类。它是在使用闭包本地之前创建的,因为访问“父”方法中的本地实际必须实际使用闭包类。 – svick 2012-02-22 12:54:01

0

我前几天测试了这个(通过使用StopWatch类)。至于我可以告诉那里是直接调用的方法之间的性能没有明显的区别...

SomeMethod(); 

...或者通过一个匿名方法...

() => SomeMethod(); 
+2

编译器会优化创建匿名类来保存匿名方法的需求,因为没有变量被关闭。 – 2012-02-22 12:51:24

+0

有趣的是,我认为编译器可能会有一个手。两个问题:如果匿名方法的主体在其范围外引用变量,是否会引入开销?而且,如果是的话,什么时候会遇到这种开销?在创建或执行匿名方法期间? – Moonshield 2012-02-22 13:09:41

2

首先,你可能不应该把很多代码放到匿名方法中。如果你为此创建了一个单独的方法,或者更好的方法,那么它会更具可读性。

至于生成的IL,如果lambda没有关闭任何变量,那么生成的IL代码与将代码放在正常的命名方法中相同(除了生成的方法具有无法形容的名称) 。另一方面,如果你关闭了某个变量,编译器会创建一个闭包类来保存该变量。本地访问的成本稍高于本地变量访问。总而言之,如果关闭一些变量,则会有小的开销(包括需要垃圾回收的更多对象)。在大多数情况下,这并不重要,并担心这将是不成熟的优化。但是,如果您认为它确实重要,则应该对代码进行概要分析。

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代码只有在创建/分配方法,委托首次小的开销。