From 1d4d5e9217743d4ca619ced34a7f1f0ff72516a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Mon, 14 Jul 2025 19:27:25 +0200 Subject: [PATCH] Avoid dynamic in AddRange detection --- .../RecyclableList.AddRange.cs | 83 +++++++++++++++---- .../RecyclableLongList.AddRange.cs | 83 +++++++++++++++---- 2 files changed, 138 insertions(+), 28 deletions(-) diff --git a/Recyclable.Collections/RecyclableList.AddRange.cs b/Recyclable.Collections/RecyclableList.AddRange.cs index ded3308..e596115 100644 --- a/Recyclable.Collections/RecyclableList.AddRange.cs +++ b/Recyclable.Collections/RecyclableList.AddRange.cs @@ -1,13 +1,74 @@ using System.Collections; using System.Numerics; using System.Runtime.CompilerServices; +using System.Linq.Expressions; +using System.Reflection; namespace Recyclable.Collections { - public static class zRecyclableListAddRange - { - private static void AddRangeEnumerated(this RecyclableList list, IEnumerable items, int growByCount) - { + public static class zRecyclableListAddRange + { + private static class AddRangeHelper + { + internal static readonly Type? DictionaryType; + internal static readonly Type? SortedListType; + internal static readonly Action, IEnumerable>? DictionaryAdder; + internal static readonly Action, IEnumerable>? SortedListAdder; + + static AddRangeHelper() + { + var itemType = typeof(T); + if (itemType.IsGenericType) + { + var genericDefinition = itemType.GetGenericTypeDefinition(); + if (genericDefinition == typeof(KeyValuePair<,>)) + { + var args = itemType.GetGenericArguments(); + DictionaryType = typeof(RecyclableDictionary<,>).MakeGenericType(args); + + var listType = typeof(RecyclableList<>).MakeGenericType(itemType); + var method = typeof(zRecyclableListAddRange).GetMethod( + nameof(AddRange), + BindingFlags.NonPublic | BindingFlags.Static, + null, + new[] { listType, DictionaryType }, + null)!.MakeGenericMethod(args); + + var listParam = Expression.Parameter(typeof(RecyclableList), "list"); + var itemsParam = Expression.Parameter(typeof(IEnumerable), "items"); + var call = Expression.Call(method, + Expression.Convert(listParam, listType), + Expression.Convert(itemsParam, DictionaryType)); + + DictionaryAdder = Expression.Lambda, IEnumerable>>(call, listParam, itemsParam).Compile(); + } + else if (genericDefinition == typeof(ValueTuple<,>)) + { + var args = itemType.GetGenericArguments(); + SortedListType = typeof(RecyclableSortedList<,>).MakeGenericType(args); + + var listType = typeof(RecyclableList<>).MakeGenericType(itemType); + var method = typeof(zRecyclableListAddRange).GetMethod( + nameof(AddRange), + BindingFlags.NonPublic | BindingFlags.Static, + null, + new[] { listType, SortedListType }, + null)!.MakeGenericMethod(args); + + var listParam = Expression.Parameter(typeof(RecyclableList), "list"); + var itemsParam = Expression.Parameter(typeof(IEnumerable), "items"); + var call = Expression.Call(method, + Expression.Convert(listParam, listType), + Expression.Convert(itemsParam, SortedListType)); + + SortedListAdder = Expression.Lambda, IEnumerable>>(call, listParam, itemsParam).Compile(); + } + } + } + } + + private static void AddRangeEnumerated(this RecyclableList list, IEnumerable items, int growByCount) + { var enumerator = items.GetEnumerator(); if (!enumerator.MoveNext()) { @@ -577,19 +638,13 @@ namespace Recyclable.Collections { AddRange(list, sourceQueue); } - else if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(KeyValuePair<,>) - && items.GetType().IsGenericType && items.GetType().GetGenericTypeDefinition() == typeof(RecyclableDictionary<,>)) + else if (AddRangeHelper.DictionaryAdder != null && AddRangeHelper.DictionaryType!.IsInstanceOfType(items)) { - dynamic dynList = list; - dynamic dynDict = items; - AddRange(dynList, dynDict); + AddRangeHelper.DictionaryAdder!(list, items); } - else if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(ValueTuple<,>) - && items.GetType().IsGenericType && items.GetType().GetGenericTypeDefinition() == typeof(RecyclableSortedList<,>)) + else if (AddRangeHelper.SortedListAdder != null && AddRangeHelper.SortedListType!.IsInstanceOfType(items)) { - dynamic dynList = list; - dynamic dynSortedList = items; - AddRange(dynList, dynSortedList); + AddRangeHelper.SortedListAdder!(list, items); } else if (items is IReadOnlyList sourceIReadOnlyList) { diff --git a/Recyclable.Collections/RecyclableLongList.AddRange.cs b/Recyclable.Collections/RecyclableLongList.AddRange.cs index 88533ea..321e28b 100644 --- a/Recyclable.Collections/RecyclableLongList.AddRange.cs +++ b/Recyclable.Collections/RecyclableLongList.AddRange.cs @@ -1,14 +1,75 @@ using System.Collections; using System.Numerics; using System.Runtime.CompilerServices; +using System.Linq.Expressions; +using System.Reflection; using Recyclable.Collections.Pools; namespace Recyclable.Collections { - public static class zRecyclableLongListAddRange - { - private static void AddRangeEnumerated(this RecyclableLongList targetList, IEnumerable items) - { + public static class zRecyclableLongListAddRange + { + private static class AddRangeHelper + { + internal static readonly Type? DictionaryType; + internal static readonly Type? SortedListType; + internal static readonly Action, IEnumerable>? DictionaryAdder; + internal static readonly Action, IEnumerable>? SortedListAdder; + + static AddRangeHelper() + { + var itemType = typeof(T); + if (itemType.IsGenericType) + { + var genericDefinition = itemType.GetGenericTypeDefinition(); + if (genericDefinition == typeof(KeyValuePair<,>)) + { + var args = itemType.GetGenericArguments(); + DictionaryType = typeof(RecyclableDictionary<,>).MakeGenericType(args); + + var listType = typeof(RecyclableLongList<>).MakeGenericType(itemType); + var method = typeof(zRecyclableLongListAddRange).GetMethod( + nameof(AddRange), + BindingFlags.NonPublic | BindingFlags.Static, + null, + new[] { listType, DictionaryType }, + null)!.MakeGenericMethod(args); + + var listParam = Expression.Parameter(typeof(RecyclableLongList), "list"); + var itemsParam = Expression.Parameter(typeof(IEnumerable), "items"); + var call = Expression.Call(method, + Expression.Convert(listParam, listType), + Expression.Convert(itemsParam, DictionaryType)); + + DictionaryAdder = Expression.Lambda, IEnumerable>>(call, listParam, itemsParam).Compile(); + } + else if (genericDefinition == typeof(ValueTuple<,>)) + { + var args = itemType.GetGenericArguments(); + SortedListType = typeof(RecyclableSortedList<,>).MakeGenericType(args); + + var listType = typeof(RecyclableLongList<>).MakeGenericType(itemType); + var method = typeof(zRecyclableLongListAddRange).GetMethod( + nameof(AddRange), + BindingFlags.NonPublic | BindingFlags.Static, + null, + new[] { listType, SortedListType }, + null)!.MakeGenericMethod(args); + + var listParam = Expression.Parameter(typeof(RecyclableLongList), "list"); + var itemsParam = Expression.Parameter(typeof(IEnumerable), "items"); + var call = Expression.Call(method, + Expression.Convert(listParam, listType), + Expression.Convert(itemsParam, SortedListType)); + + SortedListAdder = Expression.Lambda, IEnumerable>>(call, listParam, itemsParam).Compile(); + } + } + } + } + + private static void AddRangeEnumerated(this RecyclableLongList targetList, IEnumerable items) + { using IEnumerator enumerator = items.GetEnumerator(); if (!enumerator.MoveNext()) { @@ -438,18 +499,12 @@ namespace Recyclable.Collections targetList.AddRange(sourceQueue); return; - case var _ when typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(KeyValuePair<,>) - && items.GetType().IsGenericType && items.GetType().GetGenericTypeDefinition() == typeof(RecyclableDictionary<,>): - dynamic dynTargetList = targetList; - dynamic dynDict = items; - AddRange(dynTargetList, dynDict); + case var _ when AddRangeHelper.DictionaryAdder != null && AddRangeHelper.DictionaryType!.IsInstanceOfType(items): + AddRangeHelper.DictionaryAdder!(targetList, items); return; - case var _ when typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(ValueTuple<,>) - && items.GetType().IsGenericType && items.GetType().GetGenericTypeDefinition() == typeof(RecyclableSortedList<,>): - dynamic dynTargetList2 = targetList; - dynamic dynSortedList = items; - AddRange(dynTargetList2, dynSortedList); + case var _ when AddRangeHelper.SortedListAdder != null && AddRangeHelper.SortedListType!.IsInstanceOfType(items): + AddRangeHelper.SortedListAdder!(targetList, items); return; default: