如何在C#中使用LINQ填充列表?

dfty9e19  于 2024-01-03  发布在  C#
关注(0)|答案(1)|浏览(247)

最近我写了一些类似于下面的代码

  1. namespace Foo
  2. {
  3. public struct Bar
  4. {
  5. public float val;
  6. public Bar(float val) { this.val = val; }
  7. }
  8. internal class Program
  9. {
  10. static void Main(string[] args)
  11. {
  12. int rows = 3;
  13. int cols = 4;
  14. List<List<Bar>> mat = Enumerable.Repeat(
  15. Enumerable.Repeat(default(Bar), cols).ToList(),
  16. rows
  17. ).ToList();
  18. foreach(var i in Enumerable.Range(0, rows * cols))
  19. {
  20. int row = i / cols;
  21. int col = i % cols;
  22. mat[row][col] = new Bar((float)i);
  23. }
  24. foreach (var row in Enumerable.Range(0, rows))
  25. {
  26. foreach (var col in Enumerable.Range(0, cols))
  27. {
  28. Console.Write(mat[row][col].val + " ");
  29. }
  30. Console.Write("\n");
  31. }
  32. }
  33. }
  34. }

字符串
这是一个令人惊讶的结果,

  1. 8 9 10 11
  2. 8 9 10 11
  3. 8 9 10 11


我认为问题在于LINQ表达式。内部LINQ表达式是正确的,因为Barstruct,它在重复struct时使用值语义,但外部LINQ表达式将重复引用同一个List<T>,因为List<T>是一个类,因此使用引用语义。
我的问题是(1)上面对这个问题的解释正确吗?(2)在不使用显式嵌套循环的情况下初始化List<List<T>>的更好方法是什么?

toe95027

toe950271#

外部列表的内部列表包含相同的val值的原因是Enumerable.Repeat<List<Bar>>(List<Bar>, Int32)的第一个参数只计算一次,所以它们实际上是相同的列表重复rows次。
为了让自己相信这一点,你可以使用object.ReferenceEquals()来Assertmat的所有项都是相等的:

  1. Assert.That(mat.All(row => object.ReferenceEquals(row, mat[0]))); // Does not throw.

字符串
因此,foreach(var i in Enumerable.Range(0, rows * cols))循环会多次覆盖该列表的内容,最终迭代的内容将获胜。
演示小提琴#1 here
假设你想要不同的列表,包含以下内容:

  1. 0 1 2 3
  2. 4 5 6 7
  3. 8 9 10 11


您可以使用Enumerable.Range(int start, int count)的嵌套调用来简洁地创建不同列表的锯齿列表,如下所示:

  1. var mat = Enumerable.Range(0, rows)
  2. .Select(iRow => Enumerable.Range(iRow*cols, cols).Select(iCell => new Bar(iCell)).ToList())
  3. .ToList();


演示小提琴#2 here

展开查看全部

相关问题