Вбегая в препятствие с Func - PullRequest
1 голос
/ 27 октября 2010

У меня есть древовидная структура, которая представляет различные элементы фильтра для набора записей. Во время выполнения я устанавливаю тег каждого узла в тип Func. Например:

myTreeView.Nodes.Add(New TreeNode("Node1"));
myTreeView.Nodes["Node1"].Tag = New Func<MyRecordType, bool>(p=> p.Title == "test 1");

myTreeView.Nodes.Add(New TreeNode("Node2"));
myTreeView.Nodes["Node2"].Tag = New Func<MyRecordType, bool>(p=> p.Title == "test 2");

и т.д ..

Затем, когда пользователь нажимает на узел, я просто передаю тег и использую его в качестве предиката для получения моих данных:

gridView.DataSource = populateData(nodeClicked.Tag as Func<MyRecordType, bool>);

private MyRecordType[] populateData(Func<MyRecordType, bool> predicate)
{
  MyRecordType[] records;

  if(predicate == null)
     records = MyRecords.ToArray();
  else
    records = MyRecords.Where(predicate).ToArray();

  return records;
}

Все это прекрасно работало, но я добавил немного кода, который создает узлы на основе набора записей. Это выглядит так:

var users = DB.MyRecords.OrderBy(o=> o.CreatedBy).Select(s=> s.CreatedBy).Distinct();

foreach(var user in users) {
  var node = new TreeNode("Node" + user, user);
  node.Tag = new Func<MyRecordType, bool>(p=> p.CreatedBy == user);
  MyTreeView.Nodes.Add(node);
}

Проблема заключается в том, что динамически создаваемые узлы содержат один и тот же объект предиката (который, по-видимому, является тем же предикатом, созданным во время последней итерации forearch).

Я не совсем уверен, что здесь происходит. Любая идея? Спасибо миллион!

Ответы [ 3 ]

2 голосов
/ 27 октября 2010

Вы захватываете нелокальную переменную, а именно user.

Создайте временный цикл внутри цикла и вместо этого обратитесь к нему.

Пример:

foreach(var user in users) {
  var u = user;
  var node = new TreeNode("Node" + user, user);
  node.Tag = new Func<MyRecordType, bool>(p=> p.CreatedBy == u);
  MyTreeView.Nodes.Add(node);
}
0 голосов
/ 27 октября 2010

Когда вы создаете объект Func <>, он не сохраняет текущее значение пользовательской переменной и скорее работает с переменной, общей для итерации цикла. Это будет работать, однако, если вы создадите новую копию переменной внутри цикла.

Например, вместо:

        foreach (string s in new string[] { "1", "2", "3" })
            funcs.Add(() => s);

использование

        foreach (string s in new string[] { "1", "2", "3" })
        {
            string s1 = s;
            funcs.Add(() => s1);
        }
0 голосов
/ 27 октября 2010
(Func<MyRecordType, bool>)(p=> p.CreatedBy == user);

должно работать.

...