简体   繁体   English

C#,WinForms:ListBox.Items.Add 生成 OutOfMemoryException,为什么?

[英]C#, WinForms: ListBox.Items.Add generates an OutOfMemoryException, why?

First off, I found the solution to the exception.首先,我找到了异常的解决方案。 I'm more curious why it generated the specific exception it did.我更好奇它为什么会产生它所做的特定异常。

In my scenario I'm adding a POCO to a ListBox like so:在我的场景中,我将POCO添加到 ListBox 中,如下所示:

myListBox.Items.Add(myPOCO);

This was generating an OutOfMemoryException .这是产生一个OutOfMemoryException The problem was that ToString off the POCO was returning null .问题是 POCO 的ToString返回null I added a string.IsNullOrEmpty check to return a "safe" value when null and the exception went away.我添加了一个string.IsNullOrEmpty检查以在 null 和异常消失时返回“安全”值。

Why does this generate an OutOfMemoryException and not something else (say a NullReferenceException )?为什么这会生成OutOfMemoryException而不是其他东西(比如NullReferenceException )?

EDIT : Items are added in a for loop.编辑:在 for 循环中添加项目。

Full call stack (company-specific references removed) is below.完整的调用堆栈(删除了公司特定的引用)如下。 One thing to note -- the list box is empty when this is called.需要注意的一件事——调用它时列表框是空的。

System.OutOfMemoryException was unhandled
  Message="List box contains too many items."
  Source="System.Windows.Forms"
  StackTrace:
       at System.Windows.Forms.ListBox.NativeAdd(Object item)
       at System.Windows.Forms.ListBox.ObjectCollection.AddInternal(Object item)
       at System.Windows.Forms.ListBox.ObjectCollection.Add(Object item)
       at <FORM>_Load(Object sender, EventArgs e) in <PATH>\<FORM>.cs:line 52
       at System.Windows.Forms.Form.OnLoad(EventArgs e)
       at System.Windows.Forms.Form.OnCreateControl()
       at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
       at System.Windows.Forms.Control.CreateControl()
       at System.Windows.Forms.Control.WmShowWindow(Message& m)
       at System.Windows.Forms.Control.WndProc(Message& m)
       at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
       at System.Windows.Forms.ContainerControl.WndProc(Message& m)
       at System.Windows.Forms.Form.WmShowWindow(Message& m)
       at System.Windows.Forms.Form.WndProc(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
       at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
       at System.Windows.Forms.SafeNativeMethods.ShowWindow(HandleRef hWnd, Int32 nCmdShow)
       at System.Windows.Forms.Control.SetVisibleCore(Boolean value)
       at System.Windows.Forms.Form.SetVisibleCore(Boolean value)
       at System.Windows.Forms.Control.set_Visible(Boolean value)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.RunDialog(Form form)
       at System.Windows.Forms.Form.ShowDialog(IWin32Window owner)
       at System.Windows.Forms.Form.ShowDialog()
       at <APP>.Program.Main() in <PATH>\Program.cs:line 25
       at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
       at System.AppDomain.nExecuteAssembly(Assembly assembly, String[] args)
       at System.Runtime.Hosting.ManifestRunner.Run(Boolean checkAptModel)
       at System.Runtime.Hosting.ManifestRunner.ExecuteAsAssembly()
       at System.Runtime.Hosting.ApplicationActivator.CreateInstance(ActivationContext activationContext, String[] activationCustomData)
       at System.Runtime.Hosting.ApplicationActivator.CreateInstance(ActivationContext activationContext)
       at System.Activator.CreateInstance(ActivationContext activationContext)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssemblyDebugInZone()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()

This is because of the way System.Windows.Forms.ListBox.NativeAdd method is implemented:这是因为System.Windows.Forms.ListBox.NativeAdd方法的实现方式:

private int NativeAdd(object item)
{
    int num = (int) base.SendMessage(0x180, 0, base.GetItemText(item));
    switch (num)
    {
        case -2:
            throw new OutOfMemoryException();

        case -1:
            throw new OutOfMemoryException(SR.GetString("ListBoxItemOverflow"));
    }
    return num;
}

The GetItemText method uses ToString() on the object which returns null and so a message is sent with null parameter, which in turn returns an invalid pointer and you enter the second case which throws the exception. GetItemText方法在返回null的对象上使用ToString() ,因此发送带有null参数的消息,这又返回一个无效指针,并且您进入了引发异常的第二种情况。

When the underlying LB_ADDSTRING Windows API call fails, WinForms always returns an OutOfMemoryException .当底层LB_ADDSTRING Windows API 调用失败时,WinForms 总是返回OutOfMemoryException A comment in the .NET Framework Reference Source explains why: .NET Framework 参考源中的注释解释了原因:

// On some platforms (e.g. Win98), the ListBox control
// appears to return LB_ERR if there are a large number (>32000)
// of items. It doesn't appear to set error codes appropriately,
// so we'll have to assume that LB_ERR corresponds to item 
// overflow.
// 
throw new OutOfMemoryException(SR.GetString(SR.ListBoxItemOverflow)); 

If you get such error then carefully check your code where you are calling listbox.Items.Add or listbox.DataSource = xxxxx .如果您收到此类错误,请仔细检查您调用listbox.Items.Addlistbox.DataSource = xxxxx Whatever Object Type is added or bound to list box must return non Null in ToString() method.无论添加或绑定到列表框的Object类型都必须在ToString()方法中返回非 Null。 By default .net returns Type name if you do not override ToString() method.默认情况下,如果不覆盖ToString()方法,.net 将返回类型名称。

If you have implemented your own version of ToString() (with override keyword) then make sure method has fail safe to returns non NULL string.如果您已经实现了自己的ToString()版本(使用 override 关键字),请确保方法具有失败安全性以返回非 NULL 字符串。 See example below how you can add extra 2 lines to avoid such odd error which many people will find totally misleading.请参阅下面的示例,如何添加额外的 2 行以避免这种奇怪的错误,许多人会发现这种错误会完全误导。

//listbox.Items.Add(new MyItem());
//--or--
//listbox.DataSource= new List<MyItem>(){ new MyItem() };

public class MyItem
{
    public string Name { get; set; }
    public string Label { get; set; }

    public override string ToString()
    {
        //Not adding below 2 lines might throw OutOfMemoryException in listbox.Items.Add or listbox.DataSOurce = somelist
        if (string.IsNullOrEmpty(Label)) //Added this check to avoid 
            return this.GetType().Name; //Return string.Empty or something other than null else you will get OutOfMemoryException error when you add or bind this object to listbox

        return Label;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM