Why can't ref var be used within a foreach loop that iterates a List<T>?
Random rand = new();
// This is fine
Span<int> numbers = new int[] { 3, 14, 15, 92, 6 };
foreach (ref var number in numbers)
{
number = rand.Next();
}
// This is not fine
List<int> nums = new() { 3, 14, 15, 92, 6 };
foreach (ref var number in nums)
{
number = rand.Next();
}
It's because the Span<T> is equipped with an enumerator that has a ref Current property, and the List<T> isn't.
Span<T>.Enumerator.Current property:
public ref T Current { get; }
List<T>.Enumerator.Current property:
public T Current { get; }
If you want, you can get access to the internal storage of a List<T> with the CollectionsMarshal.AsSpan<T> method.
Update: Actually as @GuruStron pointed out in a comment, the C# compiler doesn't even use the Span<T>.Enumerator struct, and instead it translates a foreach loop to a fast while loop. For example the code below:
foreach (ref int item in span)
{
//...
}
...is translated to:
int i = 0;
while (i < span.Length)
{
ref int item = ref span[i];
//...
i++;
}
I found the following https://github.com/dotnet/csharplang/issues/1085 :
Proposal: Support Span and ReadOnlySpan in foreach, such that:
foreach (T item in span) { ... }is equivalent to:
for (int i = 0; i < span.Length; i++) { T item = span[i]; ... }
If it is implemented with a for-loop, it makes sense to handle the ref.
Also from the Span.Enumerator Struct Microsoft Documentation:
Unlike some other enumerator structures in .NET, the Span.Enumerator:
- Does not implement the IEnumerator or IEnumerator interface. This is because Span.Enumerator is a ref struct.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With