Skip to content

MemoryExtensions.*(..., IEqualityComparer) overloads #28934

@stephentoub

Description

@stephentoub

EDITED 11/5/2024 by @stephentoub with updated proposal:

namespace System;

public static class MemoryExtensions
{
+   public static bool Contains<T>(this ReadOnlySpan<T> span, T value, IEqualityComparer<T>? comparer = null);
+   public static bool ContainsAny<T>(this ReadOnlySpan<T> span, T value0, T value1, IEqualityComparer<T>? comparer = null);
+   public static bool ContainsAny<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2, IEqualityComparer<T>? comparer = null);
+   public static bool ContainsAny<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values, IEqualityComparer<T>? comparer = null);

+   public static bool ContainsAnyExcept<T>(this ReadOnlySpan<T> span, T value, IEqualityComparer<T>? comparer = null);
+   public static bool ContainsAnyExcept<T>(this ReadOnlySpan<T> span, T value0, T value1, IEqualityComparer<T>? comparer = null);
+   public static bool ContainsAnyExcept<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2, IEqualityComparer<T>? comparer = null);
+   public static bool ContainsAnyExcept<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values, IEqualityComparer<T>? comparer = null);

+   public static int Count<T>(this ReadOnlySpan<T> span, T value, IEqualityComparer<T>? comparer = null);
+   public static int Count<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value, IEqualityComparer<T>? comparer = null);

+   public static bool EndsWith<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value, IEqualityComparer<T>? comparer = null);
+   public static bool EndsWith<T>(this ReadOnlySpan<T> span, T value, IEqualityComparer<T>? comparer = null);

+   public static bool StartsWith<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value, IEqualityComparer<T>? comparer = null);
+   public static bool StartsWith<T>(this ReadOnlySpan<T> span, T value, IEqualityComparer<T>? comparer = null);

+   public static int IndexOf<T>(this ReadOnlySpan<T> span, T value, IEqualityComparer<T>? comparer = null);
+   public static int IndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1, IEqualityComparer<T>? comparer = null);
+   public static int IndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2, IEqualityComparer<T>? comparer = null);
+   public static int IndexOfAny<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values, IEqualityComparer<T>? comparer = null);

+   public static int IndexOf<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value, IEqualityComparer<T>? comparer = null);

+   public static int IndexOfAnyExcept<T>(this ReadOnlySpan<T> span, T value, IEqualityComparer<T>? comparer = null);
+   public static int IndexOfAnyExcept<T>(this ReadOnlySpan<T> span, T value0, T value1, IEqualityComparer<T>? comparer = null);
+   public static int IndexOfAnyExcept<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2, IEqualityComparer<T>? comparer = null);
+   public static int IndexOfAnyExcept<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value, IEqualityComparer<T>? comparer = null);

+   public static int LastIndexOf<T>(this ReadOnlySpan<T> span, T value, IEqualityComparer<T>? comparer = null);
+   public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values, IEqualityComparer<T>? comparer = null);
+   public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1, IEqualityComparer<T>? comparer = null);
+   public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2, IEqualityComparer<T>? comparer = null);

+   public static int LastIndexOf<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value, IEqualityComparer<T>? comparer = null);

+   public static int LastIndexOfAnyExcept<T>(this ReadOnlySpan<T> span, T value, IEqualityComparer<T>? comparer = null);
+   public static int LastIndexOfAnyExcept<T>(this ReadOnlySpan<T> span, T value0, T value1, IEqualityComparer<T>? comparer = null);
+   public static int LastIndexOfAnyExcept<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2, IEqualityComparer<T>? comparer = null);
+   public static int LastIndexOfAnyExcept<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values, IEqualityComparer<T>? comparer = null);

+   public static void Replace<T>(this ReadOnlySpan<T> source, Span<T> destination, T oldValue, T newValue, IEqualityComparer<T>? comparer = null);

+   public static int SequenceCompareTo<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other, IComparer<T>? comparer = null);
}

Notes:

  • The above systematically adds IEqualityComparer<T>? comparer = null overloads for existing overloads without the comparer but constraining where T : IEquatable<T>?. Exceptions to this:
    1. Overloads for Trim/TrimStart/TrimEnd and Split/SplitAny as there are a lot of them, and I've not yet seen a need. The return type also complicates things, e.g. Split returns a SpanSplitEnumerable<T> that constrains the T.
    1. Overloads for this Span. Per [API Proposal]: Apply [OverloadResolutionPriority] to Span-based overloads #109549, they're no longer needed.
  • The IEquatable<T> overloads will continue to bind when no comparer is provided and the T is IEquatable. The new overloads will be used when either a comparer is provided or when T isn't IEquatable, so the signatures just naturally become usable in more places.

Old proposal:

https://github.com/dotnet/corefx/issues/27526 was reviewed and API-approved to add four overloads:

public static class MemoryExtensions
{
    bool Contains<T>(this Span<T> span, T value);
    bool Contains<T>(this ReadOnlySpan<T> span, T value);
    bool Contains<T>(this Span<T> span, T value, IEqualityComparer<T> comparer);
    bool Contains<T>(this ReadOnlySpan<T> span, T value, IEqualityComparer<T> comparer);
}

However, only the first two got implemented. The latter two were flagged as being inconsistent:
https://github.com/dotnet/corefx/issues/27526#issuecomment-422467532

The other 2 APIs are inconsistent with the rest of System.Memory public surface (none of the several hundreds other System.Memory APIs take IEqualityComparer). I assume that this was oversight during API review. We should have a new issue opened to discuss the IEqualityComparer overloads. We should either add the overloads that take IEqualityComparer everywhere in System.Memory (ie add ~30 new APIs), or nowhere. It does not make sense to add them to a random subset.

Opening this issue to track those.

cc: @GrabYourPitchforks, @ahsonkhan

Metadata

Metadata

Assignees

Labels

api-approvedAPI was approved in API review, it can be implementedarea-System.Memorybreaking-changeIssue or PR that represents a breaking API or functional change over a previous release.in-prThere is an active PR which will close this issue when it is merged

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions