diff --git a/.ncrunch/Avalonia.Controls.UnitTests.net47.v3.ncrunchproject b/.ncrunch/Avalonia.Controls.UnitTests.net47.v3.ncrunchproject index e9d39b0c74..f30a20df78 100644 --- a/.ncrunch/Avalonia.Controls.UnitTests.net47.v3.ncrunchproject +++ b/.ncrunch/Avalonia.Controls.UnitTests.net47.v3.ncrunchproject @@ -3,5 +3,6 @@ MissingOrIgnoredProjectReference + Avalonia.Controls.UnitTests.TimePickerTests \ No newline at end of file diff --git a/src/Avalonia.Base/Collections/AvaloniaList.cs b/src/Avalonia.Base/Collections/AvaloniaList.cs index f201cfab1f..d43b4e04bb 100644 --- a/src/Avalonia.Base/Collections/AvaloniaList.cs +++ b/src/Avalonia.Base/Collections/AvaloniaList.cs @@ -543,7 +543,73 @@ namespace Avalonia.Collections /// void ICollection.CopyTo(Array array, int index) { - _inner.CopyTo((T[])array, index); + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (array.Rank != 1) + { + throw new ArgumentException("Multi-dimensional arrays are not supported."); + } + + if (array.GetLowerBound(0) != 0) + { + throw new ArgumentException("Non-zero lower bounds are not supported."); + } + + if (index < 0) + { + throw new ArgumentException("Invalid index."); + } + + if (array.Length - index < Count) + { + throw new ArgumentException("The target array is too small."); + } + + if (array is T[] tArray) + { + _inner.CopyTo(tArray, index); + } + else + { + // + // Catch the obvious case assignment will fail. + // We can't find all possible problems by doing the check though. + // For example, if the element type of the Array is derived from T, + // we can't figure out if we can successfully copy the element beforehand. + // + Type targetType = array.GetType().GetElementType()!; + Type sourceType = typeof(T); + if (!(targetType.IsAssignableFrom(sourceType) || sourceType.IsAssignableFrom(targetType))) + { + throw new ArgumentException("Invalid array type"); + } + + // + // We can't cast array of value type to object[], so we don't support + // widening of primitive types here. + // + object[] objects = array as object[]; + if (objects == null) + { + throw new ArgumentException("Invalid array type"); + } + + int count = _inner.Count; + try + { + for (int i = 0; i < count; i++) + { + objects[index++] = _inner[i]; + } + } + catch (ArrayTypeMismatchException) + { + throw new ArgumentException("Invalid array type"); + } + } } /// diff --git a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs index 19700cadab..d5ac01a092 100644 --- a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs +++ b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; @@ -334,5 +335,27 @@ namespace Avalonia.Base.UnitTests.Collections Assert.True(raised); } + + [Fact] + public void Can_CopyTo_Array_Of_Same_Type() + { + var target = new AvaloniaList { "foo", "bar", "baz" }; + var result = new string[3]; + + target.CopyTo(result, 0); + + Assert.Equal(target, result); + } + + [Fact] + public void Can_CopyTo_Array_Of_Base_Type() + { + var target = new AvaloniaList { "foo", "bar", "baz" }; + var result = new object[3]; + + ((IList)target).CopyTo(result, 0); + + Assert.Equal(target, result); + } } }