Skip to content

[API Proposal]: MemoryExtensions.ContainsAny{Except} #86528

Closed
@MihaZupan

Description

@MihaZupan

Background and motivation

Similar to how we have span.IndexOf(T) and span.Contains(T), I'm proposing a contains variant of IndexOfAny{Except}.
These methods would be semantically equivalent to IndexOfAny{Except} >= 0.

The motivation behind the API is similar to the original Contains (#25228, #33785 (comment)):

  • we can avoid some costs if we know that the caller doesn't care about the exact position of a match
  • ContainsAny/!ContainsAny is more readable than various combinations of IndexOfAny + >= 0 < 0 != -1 ...
Example performance numbers for IndexOfAny vs ContainsAny for a set of Ascii values
Method Length MatchAtStart Mean
IndexOfAny 1 False 1.642 ns
ContainsAny 1 False 1.206 ns
IndexOfAny 1 True 1.828 ns
ContainsAny 1 True 1.341 ns
IndexOfAny 7 False 7.455 ns
ContainsAny 7 False 5.057 ns
IndexOfAny 7 True 1.873 ns
ContainsAny 7 True 1.377 ns
IndexOfAny 8 False 2.207 ns
ContainsAny 8 False 2.064 ns
IndexOfAny 8 True 3.267 ns
ContainsAny 8 True 2.459 ns
IndexOfAny 16 False 2.189 ns
ContainsAny 16 False 2.033 ns
IndexOfAny 16 True 3.256 ns
ContainsAny 16 True 2.465 ns
IndexOfAny 32 False 2.908 ns
ContainsAny 32 False 2.613 ns
IndexOfAny 32 True 3.909 ns
ContainsAny 32 True 2.283 ns
IndexOfAny 64 False 3.445 ns
ContainsAny 64 False 3.155 ns
IndexOfAny 64 True 3.261 ns
ContainsAny 64 True 1.942 ns

API Proposal

namespace System;

public static class MemoryExtensions
{
    // Existing
    public static int IndexOf<T>(this ReadOnlySpan<T> span, T value) where T : IEquatable<T>?;
    public static bool Contains<T>(this ReadOnlySpan<T> span, T value) where T : IEquatable<T>?;
    public static int IndexOfAny<T>(this ReadOnlySpan<T> span, SearchValues<T> values) where T : IEquatable<T>?;
    public static int IndexOfAnyExcept<T>(this ReadOnlySpan<T> span, SearchValues<T> values) where T : IEquatable<T>?;
    public static int IndexOfAny(this ReadOnlySpan<char> span, SearchValues<string> values);

    // Proposed
    public static bool ContainsAny<T>(this ReadOnlySpan<T> span, SearchValues<T> values) where T : IEquatable<T>?;
    public static bool ContainsAnyExcept<T>(this ReadOnlySpan<T> span, SearchValues<T> values) where T : IEquatable<T>?;
    public static bool ContainsAny(this ReadOnlySpan<char> span, SearchValues<string> values);
}

API Usage

-if (text.IndexOfAny(s_invalidChars) >= 0)
+if (text.ContainsAny(s_invalidChars))

-if (text.IndexOfAnyExcept(s_allowedChars) >= 0)
+if (text.ContainsAnyExcept(s_allowedChars))

Alternative Designs

IndexOfAny has many overloads and variants. Should any of them also have ContainsAny variants?

static bool ContainsAnyExcept(this ReadOnlySpan<T> span, T value);
static bool ContainsAny{Except}(this ReadOnlySpan<T> span, T value0, T value1);
static bool ContainsAny{Except}(this ReadOnlySpan<T> span, T value0, T value1, value2);
static bool ContainsAny{Except}(this ReadOnlySpan<T> span, ReadOnlySpan<T> values);
static bool ContainsAny{Except}InRange(this ReadOnlySpan<T> span, T lowInclusive, T highInclusive);
static bool ContainsAny{Except}WhiteSpace(this ReadOnlySpan<T> span);
static bool ContainsNewLine(ReadOnlySpan<char> value) =>
-   value.IndexOfAny('\r', '\n') >= 0;
+   value.ContainsAny('\r', '\n');

Risks

More methods to think about - ContainsAny/ContainsAnyExcept would be a new set of overloads on a common type.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions