Skip to content

NaturalStringComparer can't be passed where a non-generic IComparer is required (ArrayList.Sort, WPF CollectionView.CustomSort, WinForms ListView sorting) #74

Closed

Description

@matt-edmondson

What's wrong

NaturalStringComparer (Sorting/NaturalStringComparer.cs:16) implements only IComparer<string?>. Several common .NET sorting APIs accept only the non-generic System.Collections.IComparer, and sorting file names naturally is a typical reason to use them:

  • WPF: ListCollectionView.CustomSort, the usual way to sort a bound ListBox or DataGrid with custom logic
  • WinForms: ListView.ListViewItemSorter and DataGridView.Sort(IComparer)
  • ArrayList.Sort, Array.Sort(Array, IComparer) and the non-generic SortedList

None of these accept the comparer today, so every caller has to write their own adapter class.

Repro (main @ f7c0884, net10.0)

using System.Collections;
using ktsu.Sorting;

var list = new ArrayList { "file10.txt", "file2.txt", "file1.txt" };
list.Sort(new NaturalStringComparer());
  • Observed: the code doesn't compile: error CS1503: Argument 1: cannot convert from 'ktsu.Sorting.NaturalStringComparer' to 'System.Collections.IComparer?'. Also, new NaturalStringComparer() is IComparer evaluates to False.
  • Expected: it compiles and sorts the list to file1.txt, file2.txt, file10.txt.

Suggested fix

Implement System.Collections.IComparer alongside the generic interface. Either:

  • add an explicit int IComparer.Compare(object? x, object? y) that forwards to Compare(string?, string?), or
  • derive from Comparer<string?>, which provides both interfaces.

Adding an interface keeps source and binary compatibility. Re-run package validation either way, because this overlaps with #72 and #73.

Acceptance criteria

  • new NaturalStringComparer() can be passed anywhere a System.Collections.IComparer is expected.
  • Through that interface it orders strings exactly as the generic Compare does, including null ordering and the ordinal tie-break from [patch] Break natural-order ties ordinally so distinct strings never compare equal #65.
  • Arguments that aren't strings throw ArgumentException, as Comparer<T> does. Two nulls compare equal, and a null sorts before any string.
  • New tests cover ArrayList.Sort (or Array.Sort(object[], IComparer)) and the wrong-type case.
  • The generic Compare still allocates nothing: Compare_AllocatesNothing still passes.

Activity

  1. matt-edmondson commented on Nov 2, 2026

    @matt-edmondson
    ContributorAuthor

    Triage


    Generated by Claude Code

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

Metadata

Metadata

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions