Closed
Description
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 ofIndexOfAny
+>= 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.