-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Open
Labels
Area-CompilersuntriagedIssues and PRs which have not yet been triaged by a leadIssues and PRs which have not yet been triaged by a lead
Milestone
Description
Version Used:
master (28 Oct 2018) using SharpLab
Steps to Reproduce:
- Create an enumerator where
Current
returns aref readonly
:
public static partial class Array
{
public static WhereArray<TSource> Where<TSource>(this TSource[] source, Func<TSource, bool> predicate)
{
if (source == null) ThrowSourceNull();
if (predicate is null) ThrowPredicateNull();
return new WhereArray<TSource>(source, predicate);
void ThrowSourceNull() => throw new ArgumentNullException(nameof(source));
void ThrowPredicateNull() => throw new ArgumentNullException(nameof(predicate));
}
public readonly struct WhereArray<TSource>
: IEnumerable<TSource>
{
readonly TSource[] source;
readonly Func<TSource, bool> predicate;
internal WhereArray(TSource[] source, Func<TSource, bool> predicate)
{
this.source = source;
this.predicate = predicate;
}
public Enumerator GetEnumerator() => new Enumerator(in this);
IEnumerator<TSource> IEnumerable<TSource>.GetEnumerator() => new Enumerator(in this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(in this);
public struct Enumerator : IEnumerator<TSource>
{
readonly TSource[] source;
readonly Func<TSource, bool> predicate;
readonly int count;
int index;
internal Enumerator(in WhereArray<TSource> enumerable)
{
source = enumerable.source;
predicate = enumerable.predicate;
count = enumerable.source.Length;
index = -1;
}
public ref readonly TSource Current => ref source[index];
TSource IEnumerator<TSource>.Current => source[index];
object IEnumerator.Current => source[index];
public bool MoveNext()
{
index++;
while (index < count)
{
if (predicate(source[index]))
return true;
index++;
}
return false;
}
public void Reset() => index = -1;
public void Dispose() { }
}
}
}
- When the iteration variable is not used:
public static class Program
{
public static void Main()
{
var a = new int[] { 0, 1, 2, 3, 4 };
foreach(var i in a.Where(_ => true))
Console.WriteLine("🌄");
}
}
The generated code is correct, where the reference
variable is of type ref int
:
NOTE: The readonly
is missing but that is expected.
Array.WhereArray<int>.Enumerator enumerator = obj.Where(<>c.<>9__0_0 ?? (<>c.<>9__0_0 = <>c.<>9.<Main>b__0_0)).GetEnumerator();
try
{
while (enumerator.MoveNext())
{
ref int reference = ref enumerator.Current;
Console.WriteLine("\ud83c\udf04");
}
}
finally
{
((IDisposable)enumerator).Dispose();
}
- When the iteration variable is used:
public static class Program
{
public static void Main()
{
var a = new int[] { 0, 1, 2, 3, 4 };
foreach(var i in a.Where(_ => true))
DoSomething(i);
}
public static void DoSomething(in int value)
=> Console.WriteLine(value);
}
The generated code is the following, where the value
variable now is of type int
. This causes a copy of the value type.
Array.WhereArray<int>.Enumerator enumerator = obj.Where(<>c.<>9__0_0 ?? (<>c.<>9__0_0 = <>c.<>9.<Main>b__0_0)).GetEnumerator();
try
{
while (enumerator.MoveNext())
{
int value = enumerator.Current;
DoSomething(ref value);
}
}
finally
{
((IDisposable)enumerator).Dispose();
}
- When 'foreach' is not used:
public class Program
{
static readonly int[] values = new int[] { 1 };
static ref readonly int GetValue() => ref values[0];
static void SetValue(in int value) => values[0] = value;
public static void Main()
{
ref readonly int a = ref GetValue();
SetValue(a);
}
}
The generated code if correct:
public static void Main()
{
SetValue(ref GetValue());
}
Metadata
Metadata
Assignees
Labels
Area-CompilersuntriagedIssues and PRs which have not yet been triaged by a leadIssues and PRs which have not yet been triaged by a lead