为什么谓词在递归函数中调用时会误解参数的值

时间:2019-04-06 16:26:06

标签: c# linq

我正在尝试创建一个继承自现有winform TreeView控件的扩展Treeview控件。 在类TreeViewEx中创建了Load()函数。在此函数中,数据源在foreach中循环。然后,此foreach在循环数据源上调用Where()扩展方法,并向其传递一个返回谓词的方法(将当前元素作为参数)。 该谓词误解了传递给它的参数值。似乎正在使用以前的参数值。

返回谓词之前方法中arg的值

value of arg in method before returning predicate

调试器输入谓词时arg的

value of arg when debugger enters predicate

最初,我认为此行为是由于我要遍历Enumerable而不是列表,所以我将不同的枚举更改为List,但没有任何变化。还试图使返回的谓词无效,但一无所获。

加载功能:


public Func<T, Func<T, bool>> GetChildrenPredicate { get; set; }
.
.
.
public virtual void Load(List<T> dataSource = null)
{
    try
    {
        if (CreateNode == null)
        {
            OnError?.Invoke(this, new ArgumentNullException("CreateNode"));
            return;
        }
        if (GetParentKey == null)
        {
            OnError?.Invoke(this, new ArgumentNullException("GetParentKey"));
            return;
        }
        if (GetChildrenPredicate == null)
        {
            OnError?.Invoke(this, new ArgumentNullException("GetChildrenPredicate"));
            return;
        }

        var finalDataSource = dataSource ?? DataSource;

        TreeNode node = null;
        BeginUpdate();
        foreach (var item in finalDataSource)
        {
            node = CreateNode(item);
            node.Tag = item;

            if (this.Nodes.Find(node.Name, true).Count() == 0)
            {
                var n = this.Nodes.Find(this.GetParentKey(item), true).FirstOrDefault() as TreeNode;

                if (n == null)
                {
                    this.Nodes.Add(node);
                }
                else
                {
                    n.Nodes.Add(node);
                }

                List<T> children = finalDataSource
                                  .ToList()                                   
                                  .Where(this.GetChildrenPredicate(item))
                                  .ToList(); //this.GetChildrenPredicate is
                                //the property func generating the 
                                //predicate set by a different class

                if (children.Count() > 0)
                {
                    // Recursively call this function for all childRows
                    Load(children);
                }

            }
        }
        EndUpdate();
    }
    catch (Exception ex)
    {
        OnError?.Invoke(this, ex);
    }
}

GetChildrenPredicate:

private Func<ORM.DataModels.Menu, bool> GetChildrenPredicate(ORM.DataModels.Menu arg)
{

    return (ORM.DataModels.Menu m) =>
    (m.Lepere == arg.Codmen) ||
    (m.Lepere == null && arg.Codmen == "_" + m.Niveau);
}

2 个答案:

答案 0 :(得分:0)

您在要检查的行上设置断点。这意味着该代码尚未执行。我可能会显示调试器缓存的上一个调用的值。

如果要检查return-expression的结果,请首先将其分配给一个临时变量。

Func<ORM.DataModels.Menu, bool> predicate =  (ORM.DataModels.Menu m) =>
    (m.Lepere == arg.Codmen) ||
    (m.Lepere == null && arg.Codmen == "_" + m.Niveau);
return predicate; // <== Set breakpoint here

也许这有助于首先将参数分配给局部变量,以强制lambda使用当前值。

string codmen = arg.Codmen;
return (ORM.DataModels.Menu m) =>
    (m.Lepere == codmen) ||
    (m.Lepere == null && codmen == "_" + m.Niveau);

答案 1 :(得分:0)

好的。我找到了解决方案。实际上,我没有意识到finalDataSource的每次调用都会覆盖Load()。我只关注谓词的怪异行为。只需使用在类中定义的全局DataSource属性即可。

List<T> children = this.DataSource.Where(this.GetChildrenPredicate(item)); //<= changed local variable finalDataSource to the defined property this.DataSource