Skip to content

Commit a7ef777

Browse files
committed
Added special method to access the internal array of a list.
1 parent 673186a commit a7ef777

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
namespace Hexa.NET.Utilities.Collections
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Reflection;
6+
using System.Reflection.Emit;
7+
8+
/// <summary>
9+
/// Provides a hacky way to access the local array of a <see cref="List{T}"/>.
10+
/// </summary>
11+
12+
internal static class ArrayAccessor<T>
13+
{
14+
/// <summary>
15+
/// Gets a delegate that can be used to retrieve the local array of a <see cref="List{T}"/>.
16+
/// </summary>
17+
public static Func<List<T>, T[]> Getter;
18+
19+
#nullable disable
20+
21+
static ArrayAccessor()
22+
{
23+
// Create a DynamicMethod to access the internal array field.
24+
#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
25+
// suppressing warning because this code is always available in runtime
26+
var dm = new DynamicMethod("get", MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(T[]), [typeof(List<T>)], typeof(ArrayAccessor<T>), true);
27+
#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
28+
var il = dm.GetILGenerator();
29+
30+
// Load List<T> argument onto the evaluation stack.
31+
il.Emit(OpCodes.Ldarg_0);
32+
33+
// Load the internal array field from the List<T> instance.
34+
il.Emit(OpCodes.Ldfld, typeof(List<T>).GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance));
35+
36+
// Return the internal array field.
37+
il.Emit(OpCodes.Ret);
38+
39+
// Create a delegate from the DynamicMethod to get the internal array.
40+
Getter = (Func<List<T>, T[]>)dm.CreateDelegate(typeof(Func<List<T>, T[]>));
41+
}
42+
43+
#nullable restore
44+
}
45+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
namespace Hexa.NET.Utilities.Collections
2+
{
3+
using System.Collections.Generic;
4+
using System.Runtime.CompilerServices;
5+
6+
/// <summary>
7+
/// Provides extension methods for <see cref="List{T}"/> and accessing the internal array.
8+
/// </summary>
9+
public static class ListExtensions
10+
{
11+
/// <summary>
12+
/// Allows to access the internal array of a <see cref="List{T}"/>.
13+
/// </summary>
14+
/// <typeparam name="T">The type of elements in the list.</typeparam>
15+
/// <param name="list">The list for which to access the internal array.</param>
16+
/// <returns>The internal array of the list.</returns>
17+
public static T[] GetInternalArray<T>(this List<T> list)
18+
{
19+
return ArrayAccessor<T>.Getter(list);
20+
}
21+
22+
/// <summary>
23+
/// Mutates the item at the specified index in the list using the provided function.
24+
/// </summary>
25+
/// <typeparam name="T">The type of elements in the list.</typeparam>
26+
/// <param name="list">The list to mutate.</param>
27+
/// <param name="index">The index of the item to mutate.</param>
28+
/// <param name="func">The function to apply to the item at the specified index.</param>
29+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
30+
public static void MutateItem<T>(this IList<T> list, int index, Func<T, T> func)
31+
{
32+
var item = list[index];
33+
item = func(item);
34+
list[index] = item;
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)