winforms icon indicating copy to clipboard operation
winforms copied to clipboard

ComboBox.ObjectCollection.CopyTo(Array destination, int index) copies Entry object, not inner item

Open abbweiman opened this issue 1 month ago • 4 comments

.NET version

.NET 8, 10 etc.

Did it work in .NET Framework?

Yes

Did it work in any of the earlier releases of .NET Core or .NET 5+?

No response

Issue description

ComboBox.ObjectCollection.CopyTo(Array destination, int index) "leaks" the Entry objects to destination, instead of copying the actual items.

It should be InnerList[i].Item here: https://github.com/dotnet/winforms/blob/2f677f9560b5258212c721c17f1e8803581608d7/src/System.Windows.Forms/System/Windows/Forms/Controls/ComboBox/ComboBox.ObjectCollection.cs#L290

cf. CopyTo(object[] destination, int arrayIndex): https://github.com/dotnet/winforms/blob/2f677f9560b5258212c721c17f1e8803581608d7/src/System.Windows.Forms/System/Windows/Forms/Controls/ComboBox/ComboBox.ObjectCollection.cs#L275

And also for example ListBox.ObjectCollection.CopyTo(Array destination, int index): https://github.com/dotnet/winforms/blob/2f677f9560b5258212c721c17f1e8803581608d7/src/System.Windows.Forms/System/Windows/Forms/Controls/ListBoxes/ListBox.ObjectCollection.cs#L289

Steps to reproduce

This code works in .NET 4.8 but throws InvalidCastException in .NET core.

        class MyItem
        {
        }
        static void Main(string[] args)
        {
            var cbo = new ComboBox();
            cbo.Items.Add(new MyItem());
            var item1 = (MyItem)cbo.Items[0];
            var arrayList = new ArrayList(cbo.Items);
            var item2 = (MyItem)arrayList[0]; // <-- InvalidCastException
        }

abbweiman avatar Nov 21 '25 10:11 abbweiman

This behavior is not a bug. If you need to copy the ComboBox items (the displayed content) into an object[] array, use the overload:

comboBox.Items.CopyTo(objectArray, arrayIndex);

This ensures you get the actual items rather than internal entries. If you need to work through the collection interface or your target array type is not object[] (for example, Entry[]), then use:

((ICollection)comboBox.Items).CopyTo(destinationArray, index);

This follows the standard ICollection.CopyTo contract, which copies the underlying collection elements as they are represented internally. The two overloads serve different purposes: one for copying the user-facing items, and the other for low-level collection semantics. Example code Ⅰ:

ComboBox comboBox = new();
comboBox.Items.AddRange(["Item1", "Item2", "Item3"]);
object[] destination = new object[comboBox.Items.Count];
((ICollection)comboBox.Items).CopyTo(destination, 0);

Example code Ⅱ:

var cbo = new ComboBox();
cbo.Items.Add(new MyItem());
var item1 = (MyItem)cbo.Items[0];
var arrayList = new ArrayList(cbo.Items);

// Repair: Check the type before casting to avoid invalid casting exceptions.
var item2 = arrayList[0] is MyItem mi ? mi : null;

SimonZhao888 avatar Nov 24 '25 03:11 SimonZhao888

Of course it's a bug!! How do you explain the difference between ComboBox and ListBox? And the regression from .NET Framework? Also what would be the point of returning Entry objects which is an internal class and thus not usable by the client?

And to be clear: I would never use ArrayList in new code. This was legacy code that stopped working when migrating from .NET Framework.

            var lb = new ListBox();
            lb.Items.Add(new MyItem());
            var arrayList = new ArrayList(lb.Items);
            var item = (MyItem)arrayList[0]; // <-- OK

            var cbo = new ComboBox();
            cbo.Items.Add(new MyItem());
            arrayList = new ArrayList(cbo.Items);
            item  = (MyItem)arrayList[0]; // <-- InvalidCastException

abbweiman avatar Nov 24 '25 07:11 abbweiman

@LeafShi1, @SimonZhao888: Can you investigate in what context this has regressed? I have a suspicion, but I would need to see an actual PR to confirm.

KlausLoeffelmann avatar Dec 01 '25 16:12 KlausLoeffelmann

@LeafShi1, @SimonZhao888: Can you investigate in what context this has regressed? I have a suspicion, but I would need to see an actual PR to confirm.

https://github.com/dotnet/winforms/pull/4193

abbweiman avatar Dec 02 '25 09:12 abbweiman