This commit is contained in:
Andy Gocke 2025-07-30 10:04:41 -04:00 committed by GitHub
commit bb0ee68841
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 167 additions and 2 deletions

View File

@ -62,6 +62,7 @@
<Compile Include="System\Linq\RightJoin.cs" />
<Compile Include="System\Linq\SegmentedArrayBuilder.cs" />
<Compile Include="System\Linq\Select.cs" />
<Compile Include="System\Linq\Select.SizeOpt.cs" />
<Compile Include="System\Linq\Select.SpeedOpt.cs" />
<Compile Include="System\Linq\SelectMany.cs" />
<Compile Include="System\Linq\SelectMany.SpeedOpt.cs" />

View File

@ -0,0 +1,97 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics;
namespace System.Linq;
public static partial class Enumerable
{
/// <summary>
/// An iterator that implements <see cref="IList{T}"/>. This is used primarily in size-optimized
/// code to turn linear-time iterators into constant-time iterators. The primary cost is
/// additional type checks, which are small compared to generic virtual calls.
/// </summary>
private sealed class SizeOptIListSelectIterator<TSource, TResult>(IList<TSource> _source, Func<TSource, TResult> _selector)
: Iterator<TResult>, IList<TResult>
{
TResult IList<TResult>.this[int index]
{
get => _selector(_source[index]);
set => ThrowHelper.ThrowNotSupportedException();
}
int ICollection<TResult>.Count => _source.Count;
bool ICollection<TResult>.IsReadOnly => true;
void ICollection<TResult>.Add(TResult item) => ThrowHelper.ThrowNotSupportedException();
void ICollection<TResult>.Clear() => ThrowHelper.ThrowNotSupportedException();
bool ICollection<TResult>.Contains(TResult item)
=> IndexOf(item) >= 0;
int IList<TResult>.IndexOf(TResult item) => IndexOf(item);
private int IndexOf(TResult item)
{
for (int i = 0; i < _source.Count; i++)
{
if (EqualityComparer<TResult>.Default.Equals(_selector(_source[i]), item))
{
return i;
}
}
return -1;
}
void ICollection<TResult>.CopyTo(TResult[] array, int arrayIndex)
{
for (int i = 0; i < _source.Count; i++)
{
array[arrayIndex + i] = _selector(_source[i]);
}
}
void IList<TResult>.Insert(int index, TResult item) => ThrowHelper.ThrowNotSupportedException();
bool ICollection<TResult>.Remove(TResult item) => ThrowHelper.ThrowNotSupportedException_Boolean();
void IList<TResult>.RemoveAt(int index) => ThrowHelper.ThrowNotSupportedException();
private protected override Iterator<TResult> Clone()
=> new SizeOptIListSelectIterator<TSource, TResult>(_source, _selector);
public override bool MoveNext()
{
var source = _source;
int index = _state - 1;
if ((uint)index < (uint)source.Count)
{
_state++;
_current = _selector(source[index]);
return true;
}
Dispose();
return false;
}
public override TResult[] ToArray()
{
TResult[] array = new TResult[_source.Count];
for (int i = 0; i < _source.Count; i++)
{
array[i] = _selector(_source[i]);
}
return array;
}
public override List<TResult> ToList()
{
List<TResult> list = new List<TResult>(_source.Count);
for (int i = 0; i < _source.Count; i++)
{
list.Add(_selector(_source[i]));
}
return list;
}
public override int GetCount(bool onlyIfCheap) => _source.Count;
}
}

View File

@ -0,0 +1,65 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace System.Linq
{
public static partial class Enumerable
{
private sealed class SizeOptIListSelectIterator<TSource, TResult>(IList<TSource> _source, Func<TSource, TResult> _selector)
: Iterator<TResult>
{
public override int GetCount(bool onlyIfCheap) => _source.Count;
public override Iterator<TResult> Skip(int count)
{
Debug.Assert(count > 0);
return new IListSkipTakeSelectIterator<TSource, TResult>(_source, _selector, count, int.MaxValue);
}
public override Iterator<TResult> Take(int count)
{
Debug.Assert(count > 0);
return new IListSkipTakeSelectIterator<TSource, TResult>(_source, _selector, 0, count - 1);
}
public override bool MoveNext()
{
var source = _source;
int index = _state - 1;
if ((uint)index < (uint)source.Count)
{
_state++;
_current = _selector(source[index]);
return true;
}
Dispose();
return false;
}
public override TResult[] ToArray()
{
TResult[] array = new TResult[_source.Count];
for (int i = 0; i < _source.Count; i++)
{
array[i] = _selector(_source[i]);
}
return array;
}
public override List<TResult> ToList()
{
List<TResult> list = new List<TResult>(_source.Count);
for (int i = 0; i < _source.Count; i++)
{
list.Add(_selector(_source[i]));
}
return list;
}
private protected override Iterator<TResult> Clone()
=> new SizeOptIListSelectIterator<TSource, TResult>(_source, _selector);
}
}
}

View File

@ -32,7 +32,9 @@ namespace System.Linq
// don't need more code, just more data structures describing the new types).
if (IsSizeOptimized && typeof(TResult).IsValueType)
{
return new IEnumerableSelectIterator<TSource, TResult>(iterator, selector);
return source is IList<TSource> il
? new SizeOptIListSelectIterator<TSource, TResult>(il, selector)
: new IEnumerableSelectIterator<TSource, TResult>(iterator, selector);
}
else
{

View File

@ -30,7 +30,7 @@ namespace System.Linq
count = 0;
}
else if (!IsSizeOptimized && source is Iterator<TSource> iterator)
else if (source is Iterator<TSource> iterator)
{
return iterator.Skip(count) ?? Empty<TSource>();
}