c# - 为什么 foreach on PlaceHolder.Controls 抛出 "Collection was modified"?

标签 c# asp.net exception foreach

protected override void Render(HtmlTextWriter writer)
{
    HtmlGenericControl div1 = new HtmlGenericControl("div"); 
    div1.Attributes.Add("class", "modalbox");
    if (!ClientVisible)
        div1.Attributes.CssStyle.Add("display", "none");
    HtmlGenericControl div2 = new HtmlGenericControl("div");
    div2.Attributes.Add("class", "modalbox-m1");
    HtmlGenericControl div3 = new HtmlGenericControl("div");
    div3.Attributes.Add("class", "modalbox-m2");
    foreach (Control c in this.Controls)
        div3.Controls.Add(c); // exception here 
    div2.Controls.Add(div3);
    div1.Controls.Add(div2);
    div1.RenderControl(writer);
}

我无法理解谁在哪里更改了 this.Controls

最佳答案

问题在于c,是Control有一个 Parent 当控件添加到 ControlCollection 时,就会建立双向关系。 。相同的控件不会存在于多个ControlCollection中,除非某些东西被破坏。

因此,当将 c 添加到 div3 时,它首先 removes itself来自先前的父容器 - 在本例中为 c.Parent.Controls,与正在迭代的 this.Controls 是同一对象。最终结果是修改异常,因为将控件添加到不同的集合会将其从迭代集合中删除..

避免此异常的最简单方法是创建集合的副本,如 IEnumerable.ToList 。新列表独立于 ControlCollection,当控件被删除时不会被修改。

// Note the "ToList()", from LINQ/Enumerable
foreach (Control c in this.Controls.ToList()) {
    div3.Controls.Add(c); // exception here 
}

但是,在 Render 中添加此类控件是“相当可疑”,并且在很多情况下,它会破坏跨回发的控件.


如果我只想将控件集合包装在代码中并且标记不会影响控件树,那么我将使用类似以下内容来生成适当的渲染输出而无需 创建新控件或以其他方式修改控件树。 HtmlTextWriter提供了生成相关标记的所有基本方法,即使它可能很乏味。

protected override void Render(HtmlTextWriter writer)
{
    writer.AddAttribute(HtmlTextWriterAttribute.Class, "modalbox");
    if (!ClientVisible) {
        writer.AddAttribute(HtmlTextWriterAttribute.Style, "display: none");
    }
    writer.RenderBeginTag(HtmlTextWriterTag.Div); // div1

    writer.AddAttribute(HtmlTextWriterAttribute.Class, "modalbox-m1");
    writer.RenderBeginTag(HtmlTextWriterTag.Div); // div2

    writer.AddAttribute(HtmlTextWriterAttribute.Class, "modalbox-m2");
    writer.RenderBeginTag(HtmlTextWriterTag.Div); // div3

    // Render children.
    // None of the control collections are altered in this process.
    // (This may render other content if sub-classing an
    //  existing control; in that case, loop and render the
    //  the children individually, if such is appropriate.)
    base.Render(writer);

    writer.RenderEndTag(); // div3   
    writer.RenderEndTag(); // div2
    writer.RenderEndTag(); // div1
}

提示:RenderBeginTag/RenderEndTag 作为堆栈工作,通过 AddAttribute 设置的属性始终应用于下一个 RenderBeginTag。


但在实践中,我强烈推荐(并且经常使用)User Controls它允许 ASCX 标记并创建声明性控制树。

关于c# - 为什么 foreach on PlaceHolder.Controls 抛出 "Collection was modified"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21640644/

相关文章:

c# - 以编程方式访问谷歌浏览器历史记录

c# - 尝试从文本框中获取 int 时出错

c# - 如何设置临时或短期表单例份验证

c++ - 如何处理异常?

java - 在 Java 中转换对象

c# - 如何在Windows中运行应用程序?

c# - 如何在 Tridion 中为 C# TBB 添加第三方 dll?

javascript - __doPostBack 在 safari 或 firefox 中不起作用,在 IE 中起作用

未应用子内容占位符中的 CSS 样式表

programming-languages - 异常(exception)最早出现在哪种语言中?