代码:
using System;
using System.Collections.Generic;
namespace so {
public abstract class Feature {
public void doIt() {
Console.WriteLine( GetType().FullName );
}
}
class A : Feature { }
class B : Feature { }
class C : Feature { }
public class SSCCE {
event EventHandler Click;
static void Main( string[] args ) {
SSCCE sscce = new SSCCE();
List<Feature> features = new List<Feature>();
features.Add( new A());
features.Add( new B() );
features.Add( new C() );
foreach ( Feature feature in features ) {
sscce.Click += ( object sender, EventArgs e ) => { feature.doIt(); };
}
sscce.Click.Invoke( null, null );
}
}
}
Run Code Online (Sandbox Code Playgroud)
预期结果:
so.A
so.B
so.C
Run Code Online (Sandbox Code Playgroud)
观察结果:
so.C
so.C
so.C
Run Code Online (Sandbox Code Playgroud)
在Java中,final在前面的关键字Feature在foreach循环允许feature在拉姆达的动作的主体之前使用的值,.doIt().
C#中的语法有什么用?
最后一个功能被lambda捕获(它是一个闭包).您应该创建局部变量以捕获每次迭代的功能:
foreach (Feature feature in features) {
Feature current = feature;
sscce.Click += (object sender, EventArgs e) => { current.doIt(); };
}
Run Code Online (Sandbox Code Playgroud)
我建议你阅读Eric Lippert博客上关闭循环变量文章.
注意:在最后一个版本的C#中修复了这个问题
要了解会发生什么,让我们来看看您的案例中生成的代码(在C#5之前).因此你的lambda使用局部变量,它不足以生成方法 - 编译器生成私有类,它捕获lambda中使用的局部变量:
private sealed class AnonymousClass
{
public Feature feature;
public void AnonymousMethod(object sender, EventArgs e)
{
this.feature.doIt();
}
}
Run Code Online (Sandbox Code Playgroud)
并修改了您的代码,以便它使用此AnonymousClass的实例并将其AnonymousMethod订阅到Click事件:
using(var enumerator = ((IEnumerable<Feature>)features).GetEnumerator())
{
AnonymousClass x = new AnonymousClass();
while(enumerator.MoveNext())
{
x.feature = (Feature)enumerator.Current;
sscce.Click += new EventHandler(x.AnonymousMethod);
}
}
Run Code Online (Sandbox Code Playgroud)
如您所见,您已多次订阅同一AnonymousClass实例的AnonymousMethod.并且该实例将具有与最后指定的特征相同的特征.现在,将当前要素复制到局部变量时会发生什么变化:
using(var enumerator = ((IEnumerable<Feature>)features).GetEnumerator())
{
while(enumerator.MoveNext())
{
AnonymousClass x = new AnonymousClass();
x.current = (Feature)enumerator.Current; // field has local variable name
sscce.Click += new EventHandler(x.AnonymousMethod);
}
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,AnonymousClass实例在每次迭代时创建,因此不同类实例的AnonymousMethods(每个都有捕获自己的特性)将处理Click事件.为什么代码不同 - 因为,正如Eric所说,闭包(即匿名类)是关闭变量的.为了在循环体中关闭局部变量,在第二种情况下,应在循环内创建匿名类的实例.
| 归档时间: |
|
| 查看次数: |
50 次 |
| 最近记录: |