C# ToolStripItemCollection.AddRange方法中存在缺陷?
我坚信在ToolStripItemCollection.AddRange的实现中是一个错误: 我用两个菜单制作了Windows窗体应用程序,每个菜单包含两个项目 (对不起,我没有足够的声誉来发布图片) 接下来,我将实现按钮单击处理程序:C# ToolStripItemCollection.AddRange方法中存在缺陷?,c#,.net,winforms,C#,.net,Winforms,我坚信在ToolStripItemCollection.AddRange的实现中是一个错误: 我用两个菜单制作了Windows窗体应用程序,每个菜单包含两个项目 (对不起,我没有足够的声誉来发布图片) 接下来,我将实现按钮单击处理程序: private void button1_Click(object sender, EventArgs e) { menu1.DropDownItems.AddRange(menu2.DropDownItems); } 并引发System.Argu
private void button1_Click(object sender, EventArgs e)
{
menu1.DropDownItems.AddRange(menu2.DropDownItems);
}
并引发System.ArgumentOutOfRangeException
我非常好奇为什么会发生这种情况,我用ILSpy反编译了ToolStripItemCollection程序集。这就是我看到的:
public void AddRange(ToolStripItemCollection toolStripItems)
{
if (toolStripItems == null)
{
throw new ArgumentNullException("toolStripItems");
}
if (this.IsReadOnly)
{
throw new NotSupportedException(SR.GetString("ToolStripItemCollectionIsReadOnly"));
}
using (new LayoutTransaction(this.owner, this.owner, PropertyNames.Items))
{
int count = toolStripItems.Count;
for (int i = 0; i < count; i++)
{
this.Add(toolStripItems[i]);
}
}
}
并最终进入ToolStripItemCollection.SetOwner:
private void SetOwner(ToolStripItem item)
{
if (this.itemsCollection && item != null)
{
if (item.Owner != null)
{
item.Owner.Items.Remove(item);
}
item.SetOwner(this.owner);
if (item.Renderer != null)
{
item.Renderer.InitializeItem(item);
}
}
}
我们可以清楚地看到,对于循环,在每个步骤中都从toolStripItems中删除item。MSDN对ToolStripItemCollection实现的IList接口发表了评论:
在连续元素的集合(如列表)中,删除元素之后的元素向上移动以占据空出的位置。如果集合已编制索引,则移动的元素的索引也会更新。。
结果,我们最终在错误索引上访问toolStripItems中的项(第二个项移动到位置0)。我说的对吗?不是100%确定问题是什么,但您可以通过将集合转换为数组(需要强制转换)来解决这个问题:
menu1.DropDownItems.AddRange(menu2.DropDownItems.Cast().ToArray());
您的版本引发错误的原因是,当第一个菜单添加到下拉集合时,它将从其迭代的集合中删除,因此出现OutOfRange异常。当然,它不是为了处理您的场景而编写的。这些项目已拥有所有者是故障模式。您必须复制集合,以便在对其进行迭代时不能对其进行修改。顺便说一句,这是一个很常见的问题。问题是:这个方法有缺陷还是我遗漏了什么。当我有机会使用适当的方法时,我不想做出难看的造型。
private void SetOwner(ToolStripItem item)
{
if (this.itemsCollection && item != null)
{
if (item.Owner != null)
{
item.Owner.Items.Remove(item);
}
item.SetOwner(this.owner);
if (item.Renderer != null)
{
item.Renderer.InitializeItem(item);
}
}
}
menu1.DropDownItems.AddRange(menu2.DropDownItems.Cast<ToolStripItem>().ToArray());