【发布时间】:2021-01-29 09:28:46
【问题描述】:
我正在编写一个 Windows 窗体程序。我对 linq 很陌生。我想订购内存中的对象列表。这些对象属于 Ctrl 类(并表示屏幕上的控件)。它们的属性包括:CtrlID、名称、X、Y、TabIndex。许多控件的 TabIndex 可能为零,因此我想按 TabIndex、Y、X 对它们进行排序,然后通过它们设置 TabIndex 从一开始递增,因此它是连续的。
我使用的代码是这样的:
int i;
Ctrl oCtrl = null;
Debug.WriteLine("Before");
for (i = 0; i < _ctrls.Count; i++)
{
oCtrl = _ctrls[i];
Debug.WriteLine(string.Format("{0},ID:{1}: Name:{2}, TabIndex:{3}, X:{4}, Y:{5} ", i.ToString(), oCtrl.CtrlID, oCtrl.Name, oCtrl.TabIndex.ToString(), oCtrl.X, oCtrl.Y));
}
var ctrls = from ctrl in _ctrls.Items
orderby ctrl.TabIndex, ctrl.Y, ctrl.X
select ctrl;
Debug.WriteLine("After");
for (i = 0; i < ctrls.Count(); i++)
{
oCtrl = ctrls.ElementAt(i);
Debug.WriteLine(string.Format("{0},ID:{1}: Name:{2}, TabIndex:{3}, X:{4}, Y:{5} ", i.ToString(), oCtrl.CtrlID, oCtrl.Name, oCtrl.TabIndex.ToString(), oCtrl.X, oCtrl.Y));
oCtrl.TabIndex = i + 1; // cos they're one-based
}
我在“输出”窗口中得到的结果如下:
0,ID:125: Name:cmd, TabIndex:0, X:124, Y:12
1,ID:126: Name:chk, TabIndex:0, X:124, Y:36
2,ID:127: Name:db, TabIndex:0, X:124, Y:60
3,ID:128: Name:LABEL1, TabIndex:0, X:8, Y:64
4,ID:129: Name:LABEL2, TabIndex:0, X:8, Y:88
5,ID:130: Name:nf, TabIndex:0, X:124, Y:84
6,ID:131: Name:ni, TabIndex:0, X:124, Y:108
7,ID:132: Name:LABEL3, TabIndex:0, X:8, Y:112
8,ID:133: Name:sc, TabIndex:0, X:124, Y:132
9,ID:134: Name:LABEL4, TabIndex:0, X:8, Y:136
10,ID:135: Name:sv, TabIndex:0, X:124, Y:156
11,ID:136: Name:LABEL5, TabIndex:0, X:8, Y:160
12,ID:137: Name:LABEL6, TabIndex:0, X:8, Y:292
13,ID:138: Name:txt, TabIndex:0, X:124, Y:288
14,ID:139: Name:LABEL7, TabIndex:0, X:8, Y:40
15,ID:140: Name:LABEL8, TabIndex:0, X:8, Y:16
After
0,ID:125: Name:cmd, TabIndex:0, X:124, Y:12
1,ID:126: Name:chk, TabIndex:0, X:124, Y:36
2,ID:127: Name:db, TabIndex:0, X:124, Y:60
3,ID:130: Name:nf, TabIndex:0, X:124, Y:84
4,ID:131: Name:ni, TabIndex:0, X:124, Y:108
5,ID:133: Name:sc, TabIndex:0, X:124, Y:132
6,ID:135: Name:sv, TabIndex:0, X:124, Y:156
7,ID:138: Name:txt, TabIndex:0, X:124, Y:288
8,ID:125: Name:cmd, TabIndex:1, X:124, Y:12
9,ID:127: Name:db, TabIndex:3, X:124, Y:60
10,ID:131: Name:ni, TabIndex:5, X:124, Y:108
11,ID:135: Name:sv, TabIndex:7, X:124, Y:156
12,ID:125: Name:cmd, TabIndex:9, X:124, Y:12
13,ID:131: Name:ni, TabIndex:11, X:124, Y:108
14,ID:125: Name:cmd, TabIndex:13, X:124, Y:12
15,ID:125: Name:cmd, TabIndex:15, X:124, Y:12
所有名为“LABEL...”的控件都丢失了,其余的一些控件是重复的。 TabIndex 显示为 > 0 的值是因为我在打印输出后设置了它的值,但是由于某些对象被引用了两次,所以新值第二次显示。如果我注释掉 orderby 子句使其成为直接选择,我会按照当前的顺序取回数据,不会出现重复项或缺少项目。
所以:
- 为什么我缺少对象?
- 为什么我有重复的对象?
- 如何让 orderby 做我期望的事情?
感谢您提供的任何帮助。
【问题讨论】:
-
首先:不要通过
IEnumerable<T>进行基于索引的访问,而只是通过foreach。或者在您的查询结果上致电ToList。第二:您是否验证过您的输入集合没有有那些重复项? -
shortest 修复是在分配给
ctrls之后添加一行ctrls = ctrls.ToList();。这里要理解的关键点是你反复做OrderBy。 @HimBromBeere 建议使用 foreach 而不是 for 可能是 更好的 解决方案。 stackoverflow.com/questions/4830039/… 将向您展示如何使用foreach获取索引 (i)。 -
谢谢两位。您能解释一下“反复执行 OrderBy”是什么意思吗?在
var ctrls = from ctrl in _ctrls.Items...语句之后 orderby 是否继续运行?即当我更改 TabIndex 的值时? -
好吧,查询并没有返回一个collection,而只是一个iterator。查看docs.microsoft.com/en-us/dotnet/standard/linq/… 以了解其中的区别。简而言之,在 iterator 上多次执行某些操作(在您的情况下调用
ElementAt)将多次执行底层 query。 -
It seems strange with an orderby clause that you would lazy evaulate it though, as you need to have ordered all the elements to work out which one comes first.了解您的 LINQ 查询更像是 recipe 而不是 meal 是关键。您已经告诉它如何订购,但实际上并没有订购。想象一下,你按名字命令你的孩子。你要第一个,然后更改他们的名字。然后你要求第二个 - 但是,唉,与此同时,他们重新订购以反映他们的新名字。因此,您可能会再次获得“第一个”。ToList强制“保持秩序,并留在那里”。
标签: c# linq duplicates