我如何解决这个lambda表达式外部变量问题?

我正在使用PropertyDescriptor和ICustomTypeDescriptor( 仍然 )尝试将WPF DataGrid绑定到一个对象,数据存储在一个Dictionary中。

因为如果你向WPF DataGrid传递一个Dictionary对象列表,它将根据字典的公共属性(Comparer,Count,Keys和Values)自动生成列,我的Person子类为Dictionary,并实现ICustomTypeDescriptor。

ICustomTypeDescriptor定义了一个返回PropertyDescriptorCollection的GetProperties方法。

PropertyDescriptor是抽象的,所以你必须将它子类化,我想我有一个构造函数,它接受Func和一个Action参数,它们委托字典中值的获取和设置。

然后我为字典中的每个Key创建一个PersonPropertyDescriptor,如下所示:

foreach (string s in this.Keys) { var descriptor = new PersonPropertyDescriptor( s, new Func(() => { return this[s]; }), new Action(o => { this[s] = o; })); propList.Add(descriptor); } 

问题是每个属性得到它自己的Func和Action但它们都共享外部变量s所以尽管DataGrid自动生成“ID”,“FirstName”,“LastName”,“Age”,“Gender”的列,它们都得到了设置为“性别”,这是foreach循环中s的最终静止值。

如何确保每个委托使用所需的字典密钥,即Func / Action实例化时的s值?

非常感谢。


这是我的其余想法,我只是在这里试验这些不是’真正’的类……

 // DataGrid binds to a People instance public class People : List { public People() { this.Add(new Person()); } } public class Person : Dictionary, ICustomTypeDescriptor { private static PropertyDescriptorCollection descriptors; public Person() { this["ID"] = "201203"; this["FirstName"] = "Bud"; this["LastName"] = "Tree"; this["Age"] = 99; this["Gender"] = "M"; } //... other ICustomTypeDescriptor members... public PropertyDescriptorCollection GetProperties() { if (descriptors == null) { var propList = new List(); foreach (string s in this.Keys) { var descriptor = new PersonPropertyDescriptor( s, new Func(() => { return this[s]; }), new Action(o => { this[s] = o; })); propList.Add(descriptor); } descriptors = new PropertyDescriptorCollection(propList.ToArray()); } return descriptors; } //... other other ICustomTypeDescriptor members... } public class PersonPropertyDescriptor : PropertyDescriptor { private Func getFunc; private Action setAction; public PersonPropertyDescriptor(string name, Func getFunc, Action setAction) : base(name, null) { this.getFunc = getFunc; this.setAction = setAction; } // other ... PropertyDescriptor members... public override object GetValue(object component) { return getFunc(); } public override void SetValue(object component, object value) { setAction(value); } } 

Marc的解决方案当然是正确的,但我想我会扩展为什么在下面。 正如我们大多数人所知,如果你在forforeach语句中声明一个变量,它只会存在于内部的内容,这使得它看起来像变量与在这样的语句的语句块中声明的变量相同,但那不对。

要更好地理解它,请采取以下for循环。 然后我将以while-formforms重新陈述“等效”循环。

 for(int i = 0; i < list.Length; i++) { string val; list[i] = list[i]++; val = list[i].ToString(); Console.WriteLine(val); } 

这在下面的while-form中有效:(它不完全相同,因为continue会采取不同的行为,但对于作用域规则,它是相同的)

 { int i = 0; while(i < list.Length) { { string val; list[i] = list[i]++; val = list[i].ToString(); Console.WriteLine(val); } i++; } } 

当以这种方式“爆炸”时,变量的范围变得更加清晰,你可以看到为什么它总是在你的程序中捕获相同的“s”值,以及为什么Marc的解决方案显示了你的变量的位置,以便一个独特的变量每次捕获。

只是:

  foreach (string s in this.Keys) { string copy = s; var descriptor = new PersonPropertyDescriptor( copy, new Func(() => { return this[copy]; }), new Action(o => { this[copy] = o; })); propList.Add(descriptor); } 

使用捕获的变量, 声明它是重要的。 因此,通过在循环内声明捕获的变量,每次迭代都会得到一个不同的捕获类实例(循环变量s在技​​术上声明在循环之外 )。

for循环中创建s的本地副本并使用它。

 for(string s in this.Keys) { string key = s; //... }