簡體   English   中英

集合已修改; 枚舉操作可能無法執行

[英]Collection was modified; enumeration operation may not execute

我的TabControl中有多個TabItem; tabItem1,tabItem2,tabItem3 ...這些是

CloseableTabItem。

如果我在tabItem1中添加一個節點並按一個按鈕為此節點創建subGraph模型,則

同一節點應在帶有按鈕的tabItem2中出現; 以便

tabItem2-Header = nodeName,nodeName = tabItem1-Header。

如果我從tabitem2中的節點按按鈕,則tabitem1應該集中。 如果我關閉

tabItem1並按相同的按鈕,應該再次加載tabItem1(這種情況發生在

SubGraphButton_Click)。

您看到此代碼有問題嗎?

  private void ChildNode_Click(object sender, RoutedEventArgs args)
  {
        System.Windows.Controls.Button button = (System.Windows.Controls.Button)sender;
        Node node = Part.FindAncestor<Node>(button);
        MyNodeData nodeData = node.Data as MyNodeData;
        foreach (TabItem item in tabControl.Items)
        {
            if (nodeData.Text == item.Header.ToString())
            {
                item.Focus();
            }
            else if (nodeData.Text != item.Header.ToString())
            {
                SubGraphButton_Click(sender, args);
            }
        }
 }
 private void SubGraphButton_Click(object sender, RoutedEventArgs args)
 {
        string activeDirectory = @"X:\SubGraph\";
        string[] files = Directory.GetFiles(activeDirectory);
        foreach (string fileName in files)
        {
            FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
            System.Windows.Controls.Button button = (System.Windows.Controls.Button)sender;
            Node node = Part.FindAncestor<Node>(button);
            MyNodeData nodeData = node.Data as MyNodeData;
            if (node != null)
            {
                if (nodeData.Text + ".epk" == fileName.Substring(12, fileName.Length - 12) && !tabControl.Items.Contains(tabItem1))
                {
                    tabControl.Items.Add(tabItem1);
                    tabItem1.Focus();
                    var model = new MyGraphLinksModel();
                    model.Modifiable = true;
                    model.HasUndoManager = true;
                    activateDiagram(myDiagram1);
                    activeDiagram.Model = model;
                    model.Name = fileName.Substring(12, fileName.Length - 12);
                    model.Name = model.Name.Substring(0, model.Name.Length - 4);
                    tabItem1.Header = model.Name;
                    activeDiagram.PartManager.UpdatesRouteDataPoints = false;
                    StreamReader reader = new StreamReader(file);
                    string contents = reader.ReadToEnd();
                    XElement root = XElement.Parse(contents);
                    activeDiagram.LayoutCompleted += LoadLinkRoutes;
                    model.Load<MyNodeData, MyLinkData>(root, "MyNodeData",   "MyLinkData");
                }
           }
  }

當您在修改集合的同時對其進行修改時,很可能會導致錯誤。 錯誤的類型及其相似性往往會根據底層集合的實際情況而有所不同。 迭代時修改List很可能會給您帶來很多錯誤(如果您對其進行大量修改,則可能導致不止一個錯誤),並且有可能超出范圍。 修改LinkedList可能會導致空指針異常,無限循環,訪問不存在的項等,但是可能性要小得多。

因為在一般情況下,出現問題的機會很大,所以這些問題的影響也很大,而且在診斷實際出了問題的地方(以及在何處)的難度很大,C#選擇在每次嘗試迭代時僅拋出異常在迭代過程中修改的集合。 這樣一來,您就不會遇到怪異的,意想不到的問題,直到問題的根本原因遠了一段時間,這些問題才會顯現出來。

有幾種不同的策略可以用來避免此問題:

  1. 遍歷與您真正想要修改的集合不同的集合。 在某些情況下,只需在序列上添加ToList調用,然后將其移至新集合即可完成。 這樣做時,要迭代的集合與要修改的集合是分開的,因此沒有錯誤。

  2. 您可以避免在foreach循環中修改實際的集合。 常見的示例是創建List或其他“進行更改”的itemsToAdd ,無論是itemsToAdditemsToRemove等。然后,您可以在循環后為所有這些項添加/刪除/任何內容。 (如果僅修改集合大小的一小部分,這將非常有效。)

  3. 某些類型的集合可以“迭代”,而無需實際使用傳統的迭代器(意味着foreach循環)。 例如,您可以使用常規的for循環來遍歷List ,並在添加或刪除項目時簡單地修改(遞增/遞減)循環變量。 如果正確完成,這通常是一個有效的選擇,但是很容易犯錯誤並出錯,因此,盡管其他選擇(略有效率)低效率,但由於對性能的要求不高,因此它們是非常好的選擇他們的簡單。

在枚舉集合時(本例中為tabControl.Items )(在foreach循環中正在執行此操作),您將不能修改它,因為這會使枚舉器無效。

導致錯誤的特定代碼行可能是

// In SubGraphButton_Click
// This line of code is called inside an enumeration of tabControl.Items
// This is not permitted!
tabControl.Items.Add(tabItem1);

從概念上講,您的代碼如下所示:

private void ChildNode_Click(object sender, RoutedEventArgs args)
{
    System.Windows.Controls.Button button = (System.Windows.Controls.Button)sender;
    Node node = Part.FindAncestor<Node>(button);
    MyNodeData nodeData = node.Data as MyNodeData;
    foreach (TabItem item in tabControl.Items)
    {
        if (nodeData.Text == item.Header.ToString())
        {
            item.Focus();
        }
        else if (nodeData.Text != item.Header.ToString())
        {
            // This line will throw an exception 
            DoSomethingThatModifiesTabControlItemsCollection()
        }
    }
 }

您無法修改要迭代的集合。

您可以將“ foreach”循環替換為簡單的“ for”循環,但是請注意在從集合中添加/刪除項目時正在運行的索引。

像這樣:

        for (int i = 0; i < tabControl.Items.Count; i++)
        {
            TabItem item = tabControl.Items[i];
            ... // your logic here
        }

另一個可能方便的選項是,不將項目添加到選項卡控件中.Items集合將其作為返回值獲取,將其保存在列表中,並在完成所有項目的迭代之后,插入您已經擁有的所有選項卡已創建到項目集合中,因此您在運行集合時無需修改它。

在foreach循環中,您調用SubGraphButton_Click,這又添加了一個新節點tabControl.Items.Add(tabItem1); 這是不允許的。 您可以改用for循環。

是的,這條線

tabControl.Items.Add(tabItem1); 

更改您在NodeClick中枚舉的集合
在枚舉世界中這是不行的

嘗試以的標准循環,但順序相反...

    for( int x = tabControl.Items.Count - 1; x>= 0; x--)
    {  
        TabItem item = tabControl.Items[x];
        if (nodeData.Text == item.Header.ToString())  
        {  
            item.Focus();  
        }  
        else if (nodeData.Text != item.Header.ToString())  
        {  
            SubGraphButton_Click(sender, args);  
        }  
    }  

以相反的順序循環避免檢查在SubGraphButton內添加的新項目。
我不知道這是否是不良影響。

當您遍歷tabControl中的TabItems時,您無法在ForEach中執行任何會導致tabControl的item集合發生更改的操作。

這是框架的局限性。 這是因為您當前正在遍歷TabItem。

因此,在您的ChildNode_Click函數中,

在您的ForEach內部

foreach (TabItem item in tabControl.Items)

你打電話給

SubGraphButton_Click(sender, args);

在該函數內部,您可以調用

tabControl.Items.Add(tabItem1);

在ForEach內部時,您無法操作Items集合。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM