ControlCollection 中的重复字符串索引是否可能?它是如何工作的?

Is duplicate string index in ControlCollection possible? And how does it work?

我在 WinForms C# 中开始一个小项目,有这样的东西:

UserControl uc1 = new UserControl();
UserControl uc2 = new UserControl();
mainPanel.Controls.Add(uc1);
mainPanel.Controls.Add(uc2);
Console.WriteLine(mainPanel.Controls["UserControl"].Width);

知道mainPanel.Controls是集合,怎么会出现UserControl - class uc1uc2的名称 -可以作为索引吗? 如果是这样,集合中的这 2 个元素具有相同的索引,即 UserControl,这甚至可能吗?或者这是 .NET Framework 的特殊之处?

如果我们看一下下面的代码,会发生什么

var uc1 = new UserControl();
var uc2 = new UserControl();

//We have to set the name, because one is not assigned automatically via code, only when added via Designer
uc1.Name = "notUnique";
uc2.Name = "notUnique";

//We add unique text values so that we can distinguish them uniquely for the purpose of this test
uc1.Text = "uc1";
uc2.Text = "uc2";
            
panel1.Controls.Add(uc1);
panel1.Controls.Add(uc2);

var control = panel1.Controls["notUnique"];

结果是controluc1。 但是如果我们交换控件的添加,那么我们先将 uc2 添加到集合中

panel1.Controls.Add(uc2);
panel1.Controls.Add(uc1);


var control = panel1.Controls["notUnique"];

那么在这种情况下 control = uc2

那么很明显,索引 return 是它找到的第一个匹配项(如果 none 匹配则为空)。

有趣的是,这意味着如果您还使用键删除控件,它将删除它从集合中找到的第一个控件。

panel1.Controls.RemoveByKey("notUnique");

您假设控件的名称必须是唯一的。但实际上并非如此,在 Control 的文档中,名称 属性 并未声明必须是唯一的。

现在有人可能会争辩说,在文档中使用了术语“key”,这是一个错误,但这需要与 Microsoft 讨论!我怀疑他们会声明它 按设计工作。 (如果他们更改代码,我会看到很多损坏的代码!)

同样有趣的是,如果你看一下方法 ControlCollection.Find

public System.Windows.Forms.Control[] Find (string key, bool searchAllChildren);

然后你会看到这将 return 一个数组。

这个合集

中是可能的

如果您查看 ControlCollection 的 the source code,您会看到采用字符串的索引器:

        public virtual Control this[string key] {
            get {
                // We do not support null and empty string as valid keys.
                if (String.IsNullOrEmpty(key)) {
                    return null;
                }

                // Search for the key in our collection
                int index = IndexOfKey(key);
                if (IsValidIndex(index)) {
                    return this[index];
                }
                else {
                    return null;
                }

            }
        }

控件保存在(数组)列表中,IndexOfKey 用于查找给定名称的第一个索引:

        public virtual int IndexOfKey(String key) {
            // Step 0 - Arg validation
            if (String.IsNullOrEmpty(key)) {
                return -1; // we dont support empty or null keys.
            }

            // step 1 - check the last cached item
            if (IsValidIndex(lastAccessedIndex))
            {
                if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) {
                    return lastAccessedIndex;
                }
            }

            // step 2 - search for the item
            for (int i = 0; i < this.Count; i ++) {
                if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) {
                    lastAccessedIndex = i;
                    return i;
                }
            }

            // step 3 - we didn't find it.  Invalidate the last accessed index and return -1.
            lastAccessedIndex = -1;
            return -1;
        }

此代码中没有任何唯一性约束..它只是“找到第一个添加该字符串的人”


旁注;查看这样的旧代码 (ArrayList) 并考虑现在我们拥有更强大的集合、LINQ、字典、命名参数时如何编写它是很有趣的。显然这是一种优化,MS 希望人们在循环中重复使用字符串查找等,所以他们保留最后使用的字符串并比较这个字符串以查看.. Whicj 反过来我猜意味着为了最好的资源使用,如果你知道你要通过相同的字符串重复访问控件(在循环中?)而是通过数字索引访问它..