Skip to content

Commit 5b534db

Browse files
committed
Optimize ToList method for .NET 9
1 parent 985cb17 commit 5b534db

4 files changed

Lines changed: 57 additions & 25 deletions

File tree

Ramstack.Structures.Tests/Collections/ReadOnlyArrayTests.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,19 @@ public void Operator_Conversions(string? value)
583583
}
584584
}
585585

586+
[TestCase(0)]
587+
[TestCase(1)]
588+
[TestCase(9)]
589+
public void Linq_ToList(int count)
590+
{
591+
var array = CreateArray(count).ToReadOnlyArray();
592+
var list = array.ToList();
593+
594+
Assert.That(list.Count, Is.EqualTo(count));
595+
Assert.That(list.Capacity, Is.EqualTo(count));
596+
Assert.That(list, Is.EqualTo(array));
597+
}
598+
586599
private static int[] CreateArray(int length) =>
587600
Enumerable.Range(0, length).ToArray();
588601
}

Ramstack.Structures/Collections/ArrayViewExtensions.cs

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ public static ArrayView<T> AsView<T>(this List<T>? list)
148148
{
149149
if (list is not null)
150150
{
151-
var array = ListAccessor<T>.GetBuffer(list);
151+
var array = ListAccessor<T>.GetArray(list);
152152
var count = Math.Min(list.Count, array.Length);
153153

154154
return new ArrayView<T>(array, 0, count);
@@ -157,26 +157,5 @@ public static ArrayView<T> AsView<T>(this List<T>? list)
157157
return ArrayView<T>.Empty;
158158
}
159159

160-
#region Inner type: ListAccessor<T>
161-
162-
/// <summary>
163-
/// Provides low-level access to the internal array buffer of a <see cref="List{T}"/>.
164-
/// </summary>
165-
/// <typeparam name="T">The type of the elements in the list.</typeparam>
166-
private static class ListAccessor<T>
167-
{
168-
/// <summary>
169-
/// Retrieves a reference to the internal array buffer of the specified <see cref="List{T}"/>.
170-
/// </summary>
171-
/// <param name="list">The list whose internal buffer is to be accessed.</param>
172-
/// <returns>
173-
/// A reference to the internal array used by the list.
174-
/// </returns>
175-
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_items")]
176-
public static extern ref T[] GetBuffer(List<T> list);
177-
}
178-
179-
#endregion
180-
181160
#endif
182161
}

Ramstack.Structures/Collections/ReadOnlyArrayExtensions.Linq.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -392,15 +392,22 @@ public static T[] ToArray<T>(this ReadOnlyArray<T> source)
392392
/// <inheritdoc cref="Enumerable.ToList{TSource}"/>
393393
public static List<T> ToList<T>(this ReadOnlyArray<T> source)
394394
{
395-
#if NET8_0_OR_GREATER
395+
#if NET9_0_OR_GREATER
396+
var list = new List<T>();
397+
398+
ListAccessor<T>.GetArray(list) = source.ToArray();
399+
ListAccessor<T>.GetCount(list) = source.Length;
400+
401+
return list;
402+
#elif NET8_0_OR_GREATER
396403
var list = new List<T>(source.Length);
397404

398405
System.Runtime.InteropServices.CollectionsMarshal.SetCount(list, source.Length);
399-
source.CopyTo(System.Runtime.InteropServices.CollectionsMarshal.AsSpan(list));
406+
source.TryCopyTo(System.Runtime.InteropServices.CollectionsMarshal.AsSpan(list));
400407

401408
return list;
402409
#else
403-
return source.Inner!.ToList();
410+
return [..source.Inner!];
404411
#endif
405412
}
406413

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
namespace Ramstack.Internal;
2+
3+
#if NET9_0_OR_GREATER
4+
5+
/// <summary>
6+
/// Provides low-level access to the internal array buffer of a <see cref="List{T}"/>.
7+
/// </summary>
8+
/// <typeparam name="T">The type of the elements in the list.</typeparam>
9+
internal static class ListAccessor<T>
10+
{
11+
/// <summary>
12+
/// Returns a reference to the internal array buffer of the specified <see cref="List{T}"/>.
13+
/// </summary>
14+
/// <param name="list">The list whose internal buffer is to be accessed.</param>
15+
/// <returns>
16+
/// A reference to the internal array used by the list.
17+
/// </returns>
18+
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_items")]
19+
public static extern ref T[] GetArray(List<T> list);
20+
21+
/// <summary>
22+
/// Returns a reference to the internal "_size" field of the specified <see cref="List{T}"/>
23+
/// representing the number of elements in the list.
24+
/// </summary>
25+
/// <param name="list">The list whose internal "_size" field is to be accessed.</param>
26+
/// <returns>
27+
/// A reference to the internal "_size" field representing the number of elements in the list.
28+
/// </returns>
29+
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_size")]
30+
public static extern ref int GetCount(List<T> list);
31+
}
32+
33+
#endif

0 commit comments

Comments
 (0)