3

次のようなツリー構造を構築したい:

 root  Id 1
   child id 2
     grandChild  id 3

以下のコードサンプル。使用するGetChildrenNodesCorrect(),と、正しい結果が得られます。しかし、GetChildrenNodesWrong()を使用すると、次のように返されます。

root  Id 1
   child id 2
     Null

私はそれToList()が遅延実行ではないことを知っており、すぐに結果を返します。誰でもこれを説明できますか?

 public class ToListTest
    {
       public static void Entry()
       {
           var toListTest = new ToListTest();

           toListTest.Test();

       }

       public void Test()
       {
           List<Node> newsList = new List<Node>
               {
                   new Node{Id = 1, ParentId = 0},
                   new Node{Id = 2, ParentId = 1},
                   new Node{Id = 3, ParentId = 2}
               };

          var root = BuildUpTree(newsList);
       }


        private TreeNode BuildUpTree(List<Node> newsList)
        {
            var root = new TreeNode { currentNode = newsList.First(n => n.ParentId == 0) };

            BuildUpTreeChildrenNodes(newsList, root);

            return root;
        }



        private void BuildUpTreeChildrenNodes(List<Node> newsList, TreeNode currentTreeNode)
        {

            currentTreeNode.Children = GetChildrenNodesWrong(newsList, currentTreeNode);

            foreach (var node in currentTreeNode.Children)
            {                
                BuildUpTreeChildrenNodes(newsList, node);    
            }


        }

        private  IEnumerable<TreeNode> GetChildrenNodesWrong(List<Node> newsList, TreeNode cuurentNode)
        {
            return newsList.Where(n => n.ParentId == cuurentNode.currentNode.Id)
                                .Select(n => new TreeNode
                                {
                                    currentNode = n
                                });
        }

        private IEnumerable<TreeNode> GetChildrenNodesCorrect(List<Node> newsList, TreeNode cuurentNode)
        {
            return GetChildrenNodesWrong(newsList, cuurentNode).ToList();
        }

       public class TreeNode
       {

           public Node currentNode { get; set; }
           public IEnumerable<TreeNode> Children { get; set; }

       }

       public class Node
       {

           public int Id { get; set; }
           public int ParentId { get; set; }

       }
    }

アップデート

デバッグでは、GetChildrenNodesWrong(),ルートを使用すると、メソッドが戻る前に子と孫の両方があります。メソッドが戻った後、ルートには子のみがあり、孫は null です。

更新 2

IMO、問題はきれいなコードに関連していない可能性があります。しかし、より直感的なコードを示すことは誰でも大歓迎です。

4

2 に答える 2

1

が評価されるたびにIEnumerable、Linq クエリが再実行されます。したがって、ツリーを計算しているときは、ノードにスペースを割り当てていますが、永久変数には割り当てていません。これは、 のforeachループでBuildUpTreeChildrenNodes、必要なノードのインスタンスで再帰関数を呼び出していないことを意味します。foreach代わりに、 ( を列挙する) ループによって作成されたノードの再インスタンス化されたバージョンで呼び出していますIEnumerable。代わりにを呼び出すToListと、ループはメモリ内にあるリストの要素を返します。IEnumerableforeach

public static を作成してからコードをデバッグすると、 を呼び出したときに、引数が目的のノードのインスタンスではないrootことがわかります。同じ ID を持ち、グラフ内の同じノードを表しますが、実際にはルート ノードに接続されていません。小切手:BuildUpTreeChildrenNodesnode

root.Children.Any(n => n.Id == node.Id) //true
root.Children.Contains(node) //false

問題を確認する最も簡単な方法は次のとおりです。

//Create a singleton Node list:
var nodeSingleton= Enumerable.Range(0, 1).Select(x => new Node { Id = x });
Console.Write(nodeSingleton.Single() == nodeSingleton.Single());

これが を返すことを期待するかもしれませんtrueが、実際にはfalse、Linq メソッドが呼び出される両方の回Singleで、シングルトン変数の遅延実行が再評価され、Nodeクラスの別のインスタンスが返されます。

ToListただし、シングルトンを呼び出すと、メモリ内のリストが取得され、Singleメソッドは の同じインスタンスを返しますNode

もっと広く言えば、このコードの問題は、命令型と関数型のコードを混同しすぎていることだと思います。非常に多くのメソッドがそうであり、メソッドがそうでないのは奇妙voidですGetChildrenNodesWrong。パラダイムの切り替えは混乱を招く可能性があるため、スタイルを選択してそれに固執する必要があると思います。

于 2013-05-10T20:23:20.803 に答える