Как я могу обойти эту внешнюю вариабельную проблему Lambda Expression?
-
01-10-2019 - |
Вопрос
Я играю с PropertyDescriptor и ICUSTOMTYPEDESSCRICTOR (еще) Попытка связывать DataGrid WPF для объекта, для которого данные хранятся в словаре.
Поскольку если вы проходите WPF DataGrid в списке объектов словаря, он будет автоматически генерировать столбцы на основе общедоступных свойств словаря (сравнительной, счетчики, ключи и значений) мой словарь подклассов моего человека и реализует ICUSTOMTYPEDEPEDESSCRICTOR.
ICUSTOMTYPEDESSCRICTOR определяет метод GetProperties, который возвращает свойство HandscriptorCollection.
PropertyDescriptor абстрактно, так что вы должны подкласс, я подумал, что у меня будет конструктор, который взял FUNC и параметры действия, которые передают получение и настройку значений в словаре.
Затем я создаю PersonPropertyDescriptor для каждого ключа в словаре, как это:
foreach (string s in this.Keys)
{
var descriptor = new PersonPropertyDescriptor(
s,
new Func<object>(() => { return this[s]; }),
new Action<object>(o => { this[s] = o; }));
propList.Add(descriptor);
}
Проблема в том, что каждое свойство получают свои собственные функции и действие, но все они делятся внешней переменной с Итак, хотя столбец TataGrid AutoGenerates для «ID», «первый случай», «фамилия», «возраст», «гендерные» все они получают и устанавливают против «пола», который является последним значением покоя с в петле FORACH.
Как я могу убедиться, что каждый делегат использует желаемый ключ словаря, т. Е. Значение S в момент создания функции функции / действия?
Весьма признателен.
Вот остальная часть моей идеи, я просто экспериментирую здесь, это не «реальные» классы ...
// DataGrid binds to a People instance
public class People : List<Person>
{
public People()
{
this.Add(new Person());
}
}
public class Person : Dictionary<string, object>, 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<PropertyDescriptor>();
foreach (string s in this.Keys)
{
var descriptor = new PersonPropertyDescriptor(
s,
new Func<object>(() => { return this[s]; }),
new Action<object>(o => { this[s] = o; }));
propList.Add(descriptor);
}
descriptors = new PropertyDescriptorCollection(propList.ToArray());
}
return descriptors;
}
//... other other ICustomTypeDescriptor members...
}
public class PersonPropertyDescriptor : PropertyDescriptor
{
private Func<object> getFunc;
private Action<object> setAction;
public PersonPropertyDescriptor(string name, Func<object> getFunc, Action<object> 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, конечно, правильно, но я подумал, что буду расширяться, почему ниже. Как большинство из нас знают, если вы объявите переменную в for
или foreach
заявление, это только живет только как то, что внутри, что делает его казаться Как и переменная такая же, как переменная, объявленная в отчете-блоке такого утверждения, но это не правильно.
Чтобы понять это лучше, возьмите следующую петлю. Затем я переполню «эквивалентный» цикл в то время как в то время как форма.
for(int i = 0; i < list.Length; i++)
{
string val;
list[i] = list[i]++;
val = list[i].ToString();
Console.WriteLine(val);
}
Это работает в то время как в то время как ниже: (это не совсем то же самое, потому что 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<object>(() => { return this[copy]; }),
new Action<object>(o => { this[copy] = o; }));
propList.Add(descriptor);
}
С захваченными переменными, именно там объявлен это важно. Поэтому, объявляя захваченную переменную внутри цикла, вы получите другой экземпляр класса Capture на итерацию (переменная петли, s
, технически заявлен за пределами петля).
Создать локальную копию s
Внутри вашего for
петля и используйте это.
for(string s in this.Keys) {
string key = s;
//...
}
Для некоторых дополнительных мыслей по этому вопросу см.
http://ericlippert.com/2009/11/12/Closing-over-the-loop-variable-consided-harmal-part-one/