Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use SearchValues<T> when your code repeatedly searches for the same set of characters, bytes, or—starting in .NET 9—substrings. Create the set once, typically in a static readonly field, then search spans with IndexOfAny or ContainsAny. This avoids rebuilding the search set on every call and lets .NET use optimized implementations where available.

What SearchValues does

SearchValues<T> is a reusable, optimized representation of a fixed set of values to search for. It is intended for cases where that same set is used frequently at run time, rather than for a set that changes on every search. Microsoft describes the type as optimized for repeatedly searching for the same values: SearchValues<T> API reference.

For span searches, IndexOfAny returns the position of the first matching value, or -1 if there is no match. ContainsAny answers the yes-or-no version of the question. These methods are useful for scanning input without creating a separate search for each candidate.

Cache the set when you reuse it

Create a SearchValues<T> instance once when the searched set is fixed and used across many calls. A static readonly field is a common choice. For example, this .NET 8-compatible pattern searches for any of three separators:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
using System.Buffers;

private static readonly SearchValues<char> Separators =
    SearchValues.Create(",;|");

static int FindSeparator(ReadOnlySpan<char> input) =>
    input.IndexOfAny(Separators);

The result is the first matching character’s index in input, or -1 when the span contains none of the separators. Keep the set cached rather than calling SearchValues.Create for every input. Microsoft’s CA1870 analyzer recommends creating and caching the instance before passing it to IndexOfAny or ContainsAny. The rule does not flag calls that search for up to five values, because those already use an optimal implementation: CA1870: Use SearchValues<T>.

Choose the API for the values you need to find

Characters and bytes: .NET 8 and later

.NET 8 introduced SearchValues<char> and SearchValues<byte> for searching spans for a fixed set of characters or bytes. This suits repeated checks for delimiters, token boundaries, or byte values. Microsoft documents the introduction in its .NET 9 libraries release notes.

Multiple substrings: .NET 9 and later

.NET 9 added SearchValues<string> for finding any of several strings within a span. Pass a StringComparison value when creating the set so the matching rules are explicit:

using System.Buffers;

private static readonly SearchValues<string> Keywords =
    SearchValues.Create(
        ["cat", "mouse", "dog", "dolphin"],
        StringComparison.OrdinalIgnoreCase);

static int FindKeyword(string input) =>
    input.AsSpan().IndexOfAny(Keywords);

This searches for the first occurrence of any listed substring without regard to ordinal case. The returned index is the start position of the first match in the input; if no listed substring occurs, the result is -1. The .NET 9 release notes describe this substring capability and note that its optimized implementation can use SIMD support on the underlying platform: Microsoft .NET 9 libraries release notes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Select comparison semantics deliberately

For protocol data, parsers, logs, and identifiers, ordinal comparison is often the appropriate choice because matching should follow string values rather than language-specific casing rules. Use StringComparison.OrdinalIgnoreCase when case should be ignored under ordinal rules. If the requirement is culture-sensitive text matching, select and validate the intended comparison mode rather than assuming ordinal matching has those semantics. The older String.IndexOfAny character-search API is documented as ordinal and case-sensitive: String.IndexOfAny API reference.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Decide whether SearchValues is worthwhile

For a one-off search, a cached reusable set may not help enough to justify the extra field. For frequent searches against the same larger set, caching is the intended pattern. The analyzer’s exception for searches of up to five values is a useful signal that direct overloads can already be optimal for small sets; compare the code you have rather than mechanically converting every search.

SIMD support and other runtime optimizations can improve a particular workload, but the result depends on the searched data, runtime, and hardware. Microsoft’s cited documentation does not publish a universal speedup percentage or latency figure. If performance matters quantitatively, benchmark representative inputs and call patterns on the target runtime and hardware, for example with BenchmarkDotNet.

Version and API quick reference

Capability Availability Use
SearchValues<char> and SearchValues<byte> Introduced in .NET 8 Repeated searches for a fixed set of characters or bytes in spans.
SearchValues<string> substring sets Added in .NET 9 Find any of several substrings, with an explicit comparison mode.
Span IndexOfAny Available on the .NET 8–11 API surface Get the first matching index, or -1 if nothing matches. API reference.
SearchValues<T>.Contains Available on the .NET 8–11 API surface Check whether one value belongs to the search set. API reference.
CA1870 analyzer Enabled as a suggestion in .NET 10 Identify opportunities to cache reusable search sets.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.