While arrays could be passed to Span<T> parameters via an implicit operator, these were not true language-level conversions. They did not participate in overload resolution, type inference, or conditional expressions, limiting the ergonomics of span-based APIs.
C# 14.0 makes span conversions first-class in the language. Arrays, strings, and Span<T> now have implicit language conversions to Span<T> and ReadOnlySpan<T>, allowing them to participate in overload resolution, type inference, and conditional expressions.
Code
C#
// Overload resolution now prefers span-based overloads
void Process(ReadOnlySpan<int> data) { }
void Process(int[] data) { }
int[] numbers = { 1, 2, 3 };
Process(numbers); // Calls ReadOnlySpan<int> overload
// Works in conditional expressions
bool useArray = true;
Span<int> buffer = stackalloc int[3];
ReadOnlySpan<int> result = useArray ? numbers : buffer;
// Type inference works with spans
T First<T>(ReadOnlySpan<T> items) => items[0];
var first = First(numbers); // T inferred as intC#
void Process(ReadOnlySpan<int> data) { }
void Process(int[] data) { }
int[] numbers = { 1, 2, 3 };
Process(numbers); // Calls int[] overload, span not preferred
// Conditional expressions could not mix arrays and spans
bool useArray = true;
Span<int> buffer = stackalloc int[3];
ReadOnlySpan<int> result = useArray ? numbers.AsSpan() : buffer; // Cast required
// Type inference did not work
T First<T>(ReadOnlySpan<T> items) => items[0];
var first = First(numbers.AsSpan()); // Explicit conversion requiredNotes
- These conversions now participate in overload resolution, type inference, and conditional expressions
Span<T>implicitly converts toReadOnlySpan<T>- Strings implicitly convert to
ReadOnlySpan<char>only (strings are immutable) - Passing an array to a single
Span<T>parameter already worked before C# 14 via an implicit operator; the change is about language-level integration