Skip to content

Implement CopyTo on synchronized view lists - #127

Open
aetos382 wants to merge 1 commit into
Cysharp:masterfrom
aetos382:issue-116
Open

aetos382 wants to merge 1 commit into
Cysharp:masterfrom
aetos382:issue-116

Conversation

@aetos382

@aetos382 aetos382 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Fix ICollection.CopyTo on synchronized view lists (fixes #116)

NotifyCollectionChangedSynchronizedViewList<TView> threw NotSupportedException from both CopyTo implementations.
Enumerable.ToList() takes the ICollection<T> fast path, so ToViewList().ToList() always threw on non-empty lists.

Adds public virtual void CopyTo(Span<TView> span), overridden by the three derived types, and implements both interface methods on top of it with the documented argument validation. ObservableList<T> gains CopyTo(Span<T>).

@aetos382
aetos382 requested review from neuecc and a lite review from Copilot September 1, 2026 12:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes CopyTo behavior for synchronized view lists so they can participate in the ICollection<T>.CopyTo fast path used by Enumerable.ToList(), addressing the ToViewList().ToList() exception reported in #116.

Changes:

  • Introduces CopyTo(Span<TView>) on NotifyCollectionChangedSynchronizedViewList<TView> and wires both ICollection<T>.CopyTo and ICollection.CopyTo to it with argument validation.
  • Implements/overrides CopyTo(Span<...>) in synchronized view list implementations.
  • Adds ObservableList<T>.CopyTo(Span<T>) and comprehensive unit tests for CopyTo on ObservableList<T> and synchronized view lists.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/ObservableCollections.Tests/CopyToTest.cs Adds coverage validating CopyTo semantics across ObservableList and synchronized view lists.
src/ObservableCollections/SynchronizedViewList.cs Implements CopyTo(Span<TView>) overrides on synchronized view lists.
src/ObservableCollections/ObservableList.OptimizeView.cs Delegates optimized view CopyTo(Span<T>) to the parent list.
src/ObservableCollections/ObservableList.cs Adds CopyTo(Span<T>) and refactors array CopyTo to use spans.
src/ObservableCollections/ObservableDictionary.Views.cs Cleans up usings/formatting.
src/ObservableCollections/IObservableCollection.cs Adds CopyTo(Span<TView>) and implements generic/non-generic CopyTo for view lists.
Suppressed comments (1)

src/ObservableCollections/IObservableCollection.cs:223

  • ICollection.CopyTo(Array, int) doesn't validate that arrayIndex is within the array's upper bound / that there is sufficient capacity before copying. In particular, if the collection is empty and the target is not TView[] (e.g., object[] as used by WPF), arrayIndex > array.Length will currently succeed silently because the foreach never executes.
            if (array is TView[] typedArray)
            {
                CopyTo(typedArray.AsSpan(arrayIndex));
            }

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 135 to +143
public void CopyTo(T[] array, int arrayIndex)
{
if (array is null)
{
throw new ArgumentNullException(nameof(array));
}

CopyTo(array.AsSpan(arrayIndex));
}
Comment on lines +188 to +196
void ICollection<TView>.CopyTo(TView[] array, int arrayIndex)
{
if (array is null)
{
throw new ArgumentNullException(nameof(array));
}

CopyTo(array.AsSpan(arrayIndex));
}
Comment on lines +110 to +120
/// <summary>
/// Ensures ArgumentOutOfRangeException is thrown when arrayIndex is negative.
/// </summary>
[Fact]
public void NegativeArrayIndexThrows()
{
Action act = () => CreateSource().CopyTo(new int[3], -1);

act.Should().ThrowExactly<ArgumentOutOfRangeException>();
}
}
@aetos382

Copy link
Copy Markdown
Contributor Author

I'm going to close and immediately reopen this PR to trigger PR Harness again.
Nothing in the PR itself changes.

The goal is to verify the fix for Cysharp/Actions#173 (Cysharp/Actions#175) in a real run.
That bug made unicode-security fail with bad object whenever the PR's merge base had fallen outside the fetch-depth: 2 clone.
This PR is in exactly that state: its merge base is 93d52ac, two merges behind the current master.
The current green result is from a run made before master advanced, so it does not exercise the fix.
Re-running that old run is not a reliable check either, because it may reuse the old event and workflow revision.

@aetos382 aetos382 closed this Sep 25, 2026
@aetos382 aetos382 reopened this Sep 25, 2026
@aetos382

Copy link
Copy Markdown
Contributor Author

The re-triggered run passed unicode-security, so the fix works: https://github.com/Cysharp/ObservableCollections/actions/runs/36114419146

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SynchronizedViewList throws on ToList() / when used as IEnumerable

2 participants