Background and motivation
As of today, we do not have an API that would allow to safely initialize a List<T>'s underlying buffer directly.
While working on #80633 and investigating available ways to avoid using for (...) { list.Add(element); } in IListProvider<T>.ToList() implementations, there seemed to be a couple of viable options:
However, it appears we do already have an example of similar use case that solves the same issue: string.Create.
Re-using this pattern for List<T> would achieve safety (span cannot escape) and performance (no extra .Add() calls with state mutation, elided bounds check) at the same time without having to be hidden in CollectionsMarshal class.
API Proposal
namespace System.Collections.Generic;
public partial class List<T>
{
public static List<T> Create<TState>(int count, TState state, SpanAction<T, TState> action);
}
API Usage
partial class SelectArrayIterator<TSource, TResult>
{
public List<TResult> ToList()
{
return List<TResult>.Create(_source.Length, this, (span, instance) =>
{
TSource[] source = instance._source;
Func<TSource, TResult> selector = instance._selector;
// Assert .Length equality between span and source to elide bounds check
for (int i = 0; i < span.Length; i++)
{
span[i] = _selector(source[i]);
}
}
}
}
Alternative Designs
Risks
- Possible inconvenience when using it same as with
string.Create
- Expanding a widely used type which increases code size and compilation time?
Background and motivation
As of today, we do not have an API that would allow to safely initialize a
List<T>'s underlying buffer directly.While working on #80633 and investigating available ways to avoid using
for (...) { list.Add(element); }inIListProvider<T>.ToList()implementations, there seemed to be a couple of viable options:CollectionsMarshal.CreateList<T>(T[])#80311ICollection<T>stub that would wrap anIListProvider<T>instance in order to get passed tonew List<T>(collection)which would subsequently callCopyTo(...), achieving the goalHowever, it appears we do already have an example of similar use case that solves the same issue:
string.Create.Re-using this pattern for
List<T>would achieve safety (span cannot escape) and performance (no extra .Add() calls with state mutation, elided bounds check) at the same time without having to be hidden inCollectionsMarshalclass.API Proposal
API Usage
Alternative Designs
CollectionsMarshal.CreateList<T>(T[])#80311Risks
string.Create