C# 为什么删除listbox项会在循环中第二次崩溃?

C# 为什么删除listbox项会在循环中第二次崩溃?,c#,for-loop,listbox,crash,windows-ce,C#,For Loop,Listbox,Crash,Windows Ce,这是一个持续存在的问题;在下面的代码中,旧代码(也失败)被注释掉。新代码的行为方式相同,但: private void UpdateGUIAfterTableSend(String listboxVal) { ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend"); try { //listBoxWork.DataSource = null;

这是一个持续存在的问题;在下面的代码中,旧代码(也失败)被注释掉。新代码的行为方式相同,但:

private void UpdateGUIAfterTableSend(String listboxVal)
{
    ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend");
    try
    {
        //listBoxWork.DataSource = null; // <= This (at one time, anyway) seemed necessary to circumvent "Value does not fall within the expected range"
        //// Failing ignominiously; question at https://stackoverflow.com/questions/28439941/why-is-this-array-list-removal-code-failing
        //for (int i = listBoxWork.Items.Count - 1; i >= 0; --i) // try a foreach instead?
        //{
        //    if (listBoxWork.Items[i].ToString().Contains(listboxVal))
        //    {
        //        listBoxWork.Items.RemoveAt(i);
        //    }
        //}
        BindingSource bs = listBoxWork.DataSource as BindingSource;
        ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend#2");
        for (int i = bs.Count - 1; i >= 0; --i)
        {
            if (bs[i].ToString().Contains(listboxVal))
            {
                bs.RemoveAt(i);
                ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend#3");
            }
        }
    }
    catch (Exception ex)
    {
        String msgInnerExAndStackTrace = String.Format("{0}; Inner Ex: {1}; Stack Trace: {2}", ex.Message, ex.InnerException, ex.StackTrace);
        ExceptionLoggingService.Instance.WriteLog(String.Format("From frmMain.UpdateGUIAfterTableSend: {0}", msgInnerExAndStackTrace));
    }
}
更新 我在“RemoveAt”行之前添加了另一个日志消息,得到了我所期望的结果:

Date: 2/11/2015 1:36:53 PM
Message: About to remove listbox value DSD_3_20150209151047000 at index 0
…但之后它立即崩溃,所以Rake36肯定是对的

更新2 我有点相信这会奏效:

private void UpdateGUIAfterTableSend(String listboxVal)
{
    ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend");
    try
    {
        ExceptionLoggingService.Instance.WriteLog(String.Format("About to remove listbox value {0}", listboxVal));
        listBoxWork.Items.RemoveAt(listBoxWork.Items.IndexOf(listboxVal));
    }
    catch (Exception ex)
    {
        String msgInnerExAndStackTrace = String.Format("{0}; Inner Ex: {1}; Stack Trace: {2}", ex.Message, ex.InnerException, ex.StackTrace);
        ExceptionLoggingService.Instance.WriteLog(String.Format("From frmMain.UpdateGUIAfterTableSend: {0}", msgInnerExAndStackTrace));
    }
}
    for (int i = list.Count - 1; i > 0;  i--)
    {
        if (list[i].s.Contains(listboxVal))
        {
            list.RemoveAt(i); 
        }
    }       
    listBox1.DataSource = null;
    listBox1.Items.Clear();
    listBox1.DataSource = list;
……但当我听到“砰!”的故事,然后在日志文件中找到这个时,我的希望破灭了:

Message: About to remove listbox value DSD_3_20150209151047000

Date: 2/11/2015 1:58:18 PM
Message: From frmMain.UpdateGUIAfterTableSend: Specified argument was out of the range of valid values.; Inner Ex: ; Stack Trace:    at System.Collections.ArrayList.RemoveAt(Int32 index)
   at System.Windows.Forms.ListBox.ObjectCollection.RemoveAt(Int32 index)
   at HHS.frmMain.UpdateGUIAfterTableSend(String listboxVal)
   at HHS.frmMain.SendDeliveries()
   at HHS.frmMain.menuItemSEND_Deliveries_Click(Object sender, EventArgs e)
   at System.Windows.Forms.MenuItem.OnClick(EventArgs e)
   at System.Windows.Forms.Menu.ProcessMnuProc(Control ctlThis, WM wm, Int32 wParam, Int32 lParam)
   at System.Windows.Forms.Form.WnProc(WM wm, Int32 wParam, Int32 lParam)
   at System.Windows.Forms.Control._InternalWnProc(WM wm, Int32 wParam, Int32 lParam)
   at Microsoft.AGL.Forms.EVL.EnterMainLoop(IntPtr hwnMain)
   at System.Windows.Forms.Application.Run(Form fm)
   at HHS.Program.Main()


Date: 2/11/2015 1:58:19 PM
Message: From application-wide exception handler: System.InvalidOperationException: InvalidOperationException
   at System.Collections.ArrayList.ArrayListEnumeratorSimple.MoveNext()
   at HHS.frmMain.SendDeliveries()
   at HHS.frmMain.menuItemSEND_Deliveries_Click(Object sender, EventArgs e)
   at System.Windows.Forms.MenuItem.OnClick(EventArgs e)
   at System.Windows.Forms.Menu.ProcessMnuProc(Control ctlThis, WM wm, Int32 wParam, Int32 lParam)
   at System.Windows.Forms.Form.WnProc(WM wm, Int32 wParam, Int32 lParam)
   at System.Windows.Forms.Control._InternalWnProc(WM wm, Int32 wParam, Int32 lParam)
   at Microsoft.AGL.Forms.EVL.EnterMainLoop(IntPtr hwnMain)
   at System.Windows.Forms.Application.Run(Form fm)
   at HHS.Program.Main()
我甚至在“RemoveAt”行之前添加了以下内容:

…但没什么区别

更新3 这(灵感来源于拉维·帕特尔)也会崩溃:

private void UpdateGUIAfterTableSend(String listboxVal)
{
    ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend");
    try
    {
        BindingSource bs = new BindingSource();
        bs.DataSource = listBoxWork.DataSource;
        for (int i = bs.Count - 1; i >= 0; i--)
        {
            if (bs[i].ToString().Contains(listboxVal))
            {
                ExceptionLoggingService.Instance.WriteLog(String.Format("About to remove listbox value {0} at index {1}", listboxVal, i));
                bs.RemoveAt(i);
                ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend#3");
            }
        }
    }
    catch (Exception ex)
    {
        String msgInnerExAndStackTrace = String.Format("{0}; Inner Ex: {1}; Stack Trace: {2}", ex.Message, ex.InnerException, ex.StackTrace);
        ExceptionLoggingService.Instance.WriteLog(String.Format("From frmMain.UpdateGUIAfterTableSend: {0}", msgInnerExAndStackTrace));
    }
}
…日志文件的(相关)内容包括:

Date: 2/11/2015 2:09:59 PM
Message: About to remove listbox value DSD_3_20150209151047000 at index 0

Date: 2/11/2015 2:09:59 PM
Message: Reached frmMain.UpdateGUIAfterTableSend#3

Date: 2/11/2015 2:09:59 PM
Message: From application-wide exception handler: System.InvalidOperationException: InvalidOperationException
   at System.Collections.ArrayList.ArrayListEnumeratorSimple.MoveNext()
   at HHS.frmMain.SendDeliveries()
更新4
要了解真正的修复方法,请参见

您不能在迭代时销毁列表中的元素。您需要首先收集要删除的项,然后遍历这些项,并在过程中从原始列表中删除

有用模式:

Items.RemoveAt(Items.IndexOf(itemToDelete))

不能在迭代时销毁列表中的元素。您需要首先收集要删除的项,然后遍历这些项,并在过程中从原始列表中删除

有用模式:

Items.RemoveAt(Items.IndexOf(itemToDelete))
更新2 将try{}块中的代码替换为以下代码:

BindingSource bs = listBoxWork.DataSource as BindingSource;
List<string> values = bs.DataSource as List<string>;
values.RemoveAll(v => v.Contains(listboxVal));
bs.ResetBindings(false);
你可以忽略我下面的回答。我认为您正在进行类型转换的数据源已经是BindingSource类型,因此您对原始语句很在行<代码>BindingSource bs=listBoxWork.DataSource作为BindingSource我不知道,因为我们在这里看不到全部代码


如果您希望bindingsource具有正确的行为。你最好这样用

BindingSource bs = new BindingSource();
bs.DataSource = listBoxWork.DataSource;
使用您的原始代码进行尝试。

更新2 将try{}块中的代码替换为以下代码:

BindingSource bs = listBoxWork.DataSource as BindingSource;
List<string> values = bs.DataSource as List<string>;
values.RemoveAll(v => v.Contains(listboxVal));
bs.ResetBindings(false);
你可以忽略我下面的回答。我认为您正在进行类型转换的数据源已经是BindingSource类型,因此您对原始语句很在行<代码>BindingSource bs=listBoxWork.DataSource作为BindingSource我不知道,因为我们在这里看不到全部代码


如果您希望bindingsource具有正确的行为。你最好这样用

BindingSource bs = new BindingSource();
bs.DataSource = listBoxWork.DataSource;

请使用您的原始代码进行尝试。

异常来自与您显示的逻辑完全不同的地方。触发的是应用程序范围的异常处理程序,而不是将“从frmMain.UpdateGUIAfterTableSend”创建跟踪的异常处理程序。异常很可能是因为绑定有副作用。查看bindingsource以及附加到它的事件。 还可以在删除后添加跟踪,以便您可以看到删除确实正在进行,或者使用调试器。更新3显示删除确实正在进行


在SendDeliveries中添加更多跟踪。您的问题就在那里

异常来自与您显示的逻辑完全不同的地方。触发的是应用程序范围的异常处理程序,而不是将“从frmMain.UpdateGUIAfterTableSend”创建跟踪的异常处理程序。异常很可能是因为绑定有副作用。查看bindingsource以及附加到它的事件。 还可以在删除后添加跟踪,以便您可以看到删除确实正在进行,或者使用调试器。更新3显示删除确实正在进行

在SendDeliveries中添加更多跟踪。您的问题就在那里

如果您可以这样做:

List<string> list = (List<string>) listBoxWork.DataSource;
属于以下类别:

class AString
{
    public string s { get; set; }
    public AString(string s_) { s = s_; }
    public override string ToString()  {   return s;    }
}
那么这就行了:

private void UpdateGUIAfterTableSend(String listboxVal)
{
    ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend");
    try
    {
        ExceptionLoggingService.Instance.WriteLog(String.Format("About to remove listbox value {0}", listboxVal));
        listBoxWork.Items.RemoveAt(listBoxWork.Items.IndexOf(listboxVal));
    }
    catch (Exception ex)
    {
        String msgInnerExAndStackTrace = String.Format("{0}; Inner Ex: {1}; Stack Trace: {2}", ex.Message, ex.InnerException, ex.StackTrace);
        ExceptionLoggingService.Instance.WriteLog(String.Format("From frmMain.UpdateGUIAfterTableSend: {0}", msgInnerExAndStackTrace));
    }
}
    for (int i = list.Count - 1; i > 0;  i--)
    {
        if (list[i].s.Contains(listboxVal))
        {
            list.RemoveAt(i); 
        }
    }       
    listBox1.DataSource = null;
    listBox1.Items.Clear();
    listBox1.DataSource = list;
请注意,
数据源
被修改时,
列表框

您可以使用
BindingList
,而不是不满意的往返:

    BindingList<AString> data = new BindingList<AString>();
    foreach (AStrings in list) data.Add(s);
    listBox1.DataSource = data;

    for (int i = data.Count - 1; i > 0; i--)
    {
        if (data[i].s.Contains("8"))
        {
            data.RemoveAt(i); // (list[i]);
        }
    }
BindingList数据=新的BindingList();
foreach(列表中的字符串)数据。添加(s);
listBox1.DataSource=数据;
对于(int i=data.Count-1;i>0;i--)
{
if(数据[i].s.Contains(“8”))
{
数据删除(i);/(列表[i]);
}
}
现在,删除会立即反映出来。

如果您可以这样做:

List<string> list = (List<string>) listBoxWork.DataSource;
属于以下类别:

class AString
{
    public string s { get; set; }
    public AString(string s_) { s = s_; }
    public override string ToString()  {   return s;    }
}
那么这就行了:

private void UpdateGUIAfterTableSend(String listboxVal)
{
    ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend");
    try
    {
        ExceptionLoggingService.Instance.WriteLog(String.Format("About to remove listbox value {0}", listboxVal));
        listBoxWork.Items.RemoveAt(listBoxWork.Items.IndexOf(listboxVal));
    }
    catch (Exception ex)
    {
        String msgInnerExAndStackTrace = String.Format("{0}; Inner Ex: {1}; Stack Trace: {2}", ex.Message, ex.InnerException, ex.StackTrace);
        ExceptionLoggingService.Instance.WriteLog(String.Format("From frmMain.UpdateGUIAfterTableSend: {0}", msgInnerExAndStackTrace));
    }
}
    for (int i = list.Count - 1; i > 0;  i--)
    {
        if (list[i].s.Contains(listboxVal))
        {
            list.RemoveAt(i); 
        }
    }       
    listBox1.DataSource = null;
    listBox1.Items.Clear();
    listBox1.DataSource = list;
请注意,
数据源
被修改时,
列表框

您可以使用
BindingList
,而不是不满意的往返:

    BindingList<AString> data = new BindingList<AString>();
    foreach (AStrings in list) data.Add(s);
    listBox1.DataSource = data;

    for (int i = data.Count - 1; i > 0; i--)
    {
        if (data[i].s.Contains("8"))
        {
            data.RemoveAt(i); // (list[i]);
        }
    }
BindingList数据=新的BindingList();
foreach(列表中的字符串)数据。添加(s);
listBox1.DataSource=数据;
对于(int i=data.Count-1;i>0;i--)
{
if(数据[i].s.Contains(“8”))
{
数据删除(i);/(列表[i]);
}
}

现在删除的内容会立即反映出来。

任何使用--我而不是我--的特殊原因?@TaW也想到了这一点,在这种情况下不会有任何区别。是的,我将它改为我--它的行为完全相同(非常顽皮)。我认为Rake和Ramblin'Man是对的-请参阅更新。如果lb是数据绑定的,您甚至不应该尝试从中删除项目,而应该仅从数据源中删除!!您可能想使用组合框作为示例。您能告诉我们列表框绑定到什么吗?是arraylist吗?还有一件事,如果你用消息框替换日志消息,你会看到相同的消息吗?任何使用--我而不是我--的特殊原因?@TaW也想到了这一点,在这种情况下不会有任何区别。是的,我把它改成了我--它的行为完全相同(非常顽皮)。我认为Rake和Ramblin'Man是对的-请参阅更新。如果lb是数据绑定的,您甚至不应该尝试从中删除项目,而应该仅从数据源中删除!!您可能想使用组合框作为示例。您能告诉我们列表框绑定到什么吗?是arraylist吗?还有一件事,如果您将日志消息替换为消息框,您会看到相同的消息吗?他使用的是反向for循环。它不应该产生任何问题。您不能在foreach循环中这样做,但可以在fornext循环中这样做。所以这个答案是不正确的