-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
The C# compiler has a valuable optimization that enables code like:
ReadOnlySpan<byte> span = new byte[] { 1, 2, 3 };
to be converted into code that emits that constant data into the assembly, and then this code just creates a span directly referencing that data. However, this optimization is limited to single byte primitive types, to all of the initialization data being constant, and to immediately storing into a ReadOnlySpan<byte>
. If you do:
ReadOnlySpan<byte> span = new byte[] { 1, 2, someStaticField };
or
Span<byte> span = new byte[] { 1, 2, 3 };
or
ReadOnlySpan<char> span = new char[] { 'a', 'b', 'c' };
those will all allocate.
With #60948 and compiler support via dotnet/roslyn#61414 (or something similar), this optimization will helpfully be extended as well to char, short, ushort, int, uint, long, ulong, single, and double, such that code like:
ReadOnlySpan<char> span = new char[] { 'a', 'b', 'c' };
will similarly become non-allocating... but only when targeting .NET 7 or newer such that RuntimeHelpers.CreateSpan
is available. When that methods isn't available, this same line of code will again regress to allocating.
It's thus really easy for a developer to accidentally allocate, as this syntax is something a developer typically only writes when they're actively trying to optimize and avoid the allocation. Hopefully language syntax will help to make this simpler, clearer, and less error prone in the future (dotnet/csharplang#5295), but for now we can add an analyzer to detect these cases and warn the developer about the unexpected allocation.
The analyzer would warn when:
- Casting a
new T[]
expression to aReadOnlySpan<T>
either implicitly or explicitly, and - Either T isn't byte/sbyte/bool or CreateSpan is available and T isn't byte/sbyte/bool/char/ushort/short/uint/int/ulong/long/single/double, or
- Some of the data being used to initialize the array isn't const
We could restrict this further to only applying to properties/method following the pattern:
static ReadOnlySpan<T> Data => new T[] { ... };
or we could extend it to statement bodies as well.
The default level should probably be suggestion.
For a fixer, we could write one for the property case, changing it to instead be a static readonly array. Or we could just not have a fixer at all.