From 976c5eb78b36d36022d9613fb73a42ed9f612816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Mon, 14 Jul 2025 18:17:37 +0200 Subject: [PATCH 1/4] Add missing AddRange overloads for dictionary and sorted list --- .../RecyclableList.AddRange.cs | 20 ++++++++++++++--- .../RecyclableLongList.AddRange.cs | 22 +++++++++++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/Recyclable.Collections/RecyclableList.AddRange.cs b/Recyclable.Collections/RecyclableList.AddRange.cs index 87b00e3..ded3308 100644 --- a/Recyclable.Collections/RecyclableList.AddRange.cs +++ b/Recyclable.Collections/RecyclableList.AddRange.cs @@ -577,10 +577,24 @@ namespace Recyclable.Collections { AddRange(list, sourceQueue); } + else if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(KeyValuePair<,>) + && items.GetType().IsGenericType && items.GetType().GetGenericTypeDefinition() == typeof(RecyclableDictionary<,>)) + { + dynamic dynList = list; + dynamic dynDict = items; + AddRange(dynList, dynDict); + } + else if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(ValueTuple<,>) + && items.GetType().IsGenericType && items.GetType().GetGenericTypeDefinition() == typeof(RecyclableSortedList<,>)) + { + dynamic dynList = list; + dynamic dynSortedList = items; + AddRange(dynList, dynSortedList); + } else if (items is IReadOnlyList sourceIReadOnlyList) - { - AddRange(list, sourceIReadOnlyList); - } + { + AddRange(list, sourceIReadOnlyList); + } else if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity) && requiredAdditionalCapacity != 0) { AddRangeWithKnownCount(list, items, list._count, requiredAdditionalCapacity); diff --git a/Recyclable.Collections/RecyclableLongList.AddRange.cs b/Recyclable.Collections/RecyclableLongList.AddRange.cs index 92dcef1..88533ea 100644 --- a/Recyclable.Collections/RecyclableLongList.AddRange.cs +++ b/Recyclable.Collections/RecyclableLongList.AddRange.cs @@ -438,10 +438,24 @@ namespace Recyclable.Collections targetList.AddRange(sourceQueue); return; - default: - if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity)) - { - targetList.AddRangeWithKnownCount(items, requiredAdditionalCapacity); + 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); + 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); + return; + + default: + if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity)) + { + targetList.AddRangeWithKnownCount(items, requiredAdditionalCapacity); } else { 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 2/4] 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: From b571a2df595ce747c965038eeb95fb1037adb096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Mon, 14 Jul 2025 19:27:33 +0200 Subject: [PATCH 3/4] Use interface for AddRange optimizations --- Recyclable.Collections/IAddRangeProvider.cs | 9 +++ .../RecyclableDictionary.cs | 12 +++- .../RecyclableList.AddRange.cs | 68 +------------------ .../RecyclableLongList.AddRange.cs | 68 +------------------ .../RecyclableSortedList.cs | 12 +++- 5 files changed, 35 insertions(+), 134 deletions(-) create mode 100644 Recyclable.Collections/IAddRangeProvider.cs diff --git a/Recyclable.Collections/IAddRangeProvider.cs b/Recyclable.Collections/IAddRangeProvider.cs new file mode 100644 index 0000000..8928381 --- /dev/null +++ b/Recyclable.Collections/IAddRangeProvider.cs @@ -0,0 +1,9 @@ +namespace Recyclable.Collections +{ + internal interface IAddRangeProvider + { + void AddRangeTo(RecyclableList list); + void AddRangeTo(RecyclableLongList list); + } +} + diff --git a/Recyclable.Collections/RecyclableDictionary.cs b/Recyclable.Collections/RecyclableDictionary.cs index 33cb849..f3d3c68 100644 --- a/Recyclable.Collections/RecyclableDictionary.cs +++ b/Recyclable.Collections/RecyclableDictionary.cs @@ -6,7 +6,7 @@ using Recyclable.Collections.Pools; namespace Recyclable.Collections { - internal sealed class RecyclableDictionary : IEnumerable>, IDisposable + internal sealed class RecyclableDictionary : IEnumerable>, IDisposable, IAddRangeProvider> where TKey : notnull { internal static readonly bool _needsClearing = !typeof(TKey).IsValueType || !typeof(TValue).IsValueType; @@ -401,5 +401,15 @@ namespace Recyclable.Collections { } } + + void IAddRangeProvider>.AddRangeTo(RecyclableList> list) + { + zRecyclableListAddRange.AddRange(list, this); + } + + void IAddRangeProvider>.AddRangeTo(RecyclableLongList> list) + { + zRecyclableLongListAddRange.AddRange(list, this); + } } } diff --git a/Recyclable.Collections/RecyclableList.AddRange.cs b/Recyclable.Collections/RecyclableList.AddRange.cs index e596115..f12696f 100644 --- a/Recyclable.Collections/RecyclableList.AddRange.cs +++ b/Recyclable.Collections/RecyclableList.AddRange.cs @@ -1,71 +1,11 @@ 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 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) { @@ -638,13 +578,9 @@ namespace Recyclable.Collections { AddRange(list, sourceQueue); } - else if (AddRangeHelper.DictionaryAdder != null && AddRangeHelper.DictionaryType!.IsInstanceOfType(items)) + else if (items is IAddRangeProvider provider) { - AddRangeHelper.DictionaryAdder!(list, items); - } - else if (AddRangeHelper.SortedListAdder != null && AddRangeHelper.SortedListType!.IsInstanceOfType(items)) - { - AddRangeHelper.SortedListAdder!(list, items); + provider.AddRangeTo(list); } else if (items is IReadOnlyList sourceIReadOnlyList) { diff --git a/Recyclable.Collections/RecyclableLongList.AddRange.cs b/Recyclable.Collections/RecyclableLongList.AddRange.cs index 321e28b..825963b 100644 --- a/Recyclable.Collections/RecyclableLongList.AddRange.cs +++ b/Recyclable.Collections/RecyclableLongList.AddRange.cs @@ -1,72 +1,12 @@ 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 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) { @@ -499,12 +439,8 @@ namespace Recyclable.Collections targetList.AddRange(sourceQueue); return; - case var _ when AddRangeHelper.DictionaryAdder != null && AddRangeHelper.DictionaryType!.IsInstanceOfType(items): - AddRangeHelper.DictionaryAdder!(targetList, items); - return; - - case var _ when AddRangeHelper.SortedListAdder != null && AddRangeHelper.SortedListType!.IsInstanceOfType(items): - AddRangeHelper.SortedListAdder!(targetList, items); + case IAddRangeProvider provider: + provider.AddRangeTo(targetList); return; default: diff --git a/Recyclable.Collections/RecyclableSortedList.cs b/Recyclable.Collections/RecyclableSortedList.cs index 9b371ea..b11ca58 100644 --- a/Recyclable.Collections/RecyclableSortedList.cs +++ b/Recyclable.Collections/RecyclableSortedList.cs @@ -5,7 +5,7 @@ using Recyclable.Collections.Pools; namespace Recyclable.Collections { - internal sealed class RecyclableSortedList : IEnumerable<(TKey Key, TValue Value)>, IDisposable + internal sealed class RecyclableSortedList : IEnumerable<(TKey Key, TValue Value)>, IDisposable, IAddRangeProvider<(TKey Key, TValue Value)> where TKey : notnull { internal static readonly bool _needsClearing = !typeof(TKey).IsValueType || !typeof(TValue).IsValueType; @@ -276,5 +276,15 @@ namespace Recyclable.Collections { } } + + void IAddRangeProvider<(TKey Key, TValue Value)>.AddRangeTo(RecyclableList<(TKey Key, TValue Value)> list) + { + zRecyclableListAddRange.AddRange(list, this); + } + + void IAddRangeProvider<(TKey Key, TValue Value)>.AddRangeTo(RecyclableLongList<(TKey Key, TValue Value)> list) + { + zRecyclableLongListAddRange.AddRange(list, this); + } } } From dc0b3b92e57bbef96a2095829c3f15ec2ada02d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Mon, 14 Jul 2025 19:27:41 +0200 Subject: [PATCH 4/4] Remove AddRange interface and optimize casting --- Recyclable.Collections/IAddRangeProvider.cs | 9 -- .../RecyclableDictionary.cs | 11 +-- .../RecyclableList.AddRange.cs | 71 +++++++++++++- .../RecyclableLongList.AddRange.cs | 93 ++++++++++++++++--- .../RecyclableSortedList.cs | 11 +-- 5 files changed, 152 insertions(+), 43 deletions(-) delete mode 100644 Recyclable.Collections/IAddRangeProvider.cs diff --git a/Recyclable.Collections/IAddRangeProvider.cs b/Recyclable.Collections/IAddRangeProvider.cs deleted file mode 100644 index 8928381..0000000 --- a/Recyclable.Collections/IAddRangeProvider.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Recyclable.Collections -{ - internal interface IAddRangeProvider - { - void AddRangeTo(RecyclableList list); - void AddRangeTo(RecyclableLongList list); - } -} - diff --git a/Recyclable.Collections/RecyclableDictionary.cs b/Recyclable.Collections/RecyclableDictionary.cs index f3d3c68..88f5d19 100644 --- a/Recyclable.Collections/RecyclableDictionary.cs +++ b/Recyclable.Collections/RecyclableDictionary.cs @@ -6,7 +6,7 @@ using Recyclable.Collections.Pools; namespace Recyclable.Collections { - internal sealed class RecyclableDictionary : IEnumerable>, IDisposable, IAddRangeProvider> + internal sealed class RecyclableDictionary : IEnumerable>, IDisposable where TKey : notnull { internal static readonly bool _needsClearing = !typeof(TKey).IsValueType || !typeof(TValue).IsValueType; @@ -402,14 +402,5 @@ namespace Recyclable.Collections } } - void IAddRangeProvider>.AddRangeTo(RecyclableList> list) - { - zRecyclableListAddRange.AddRange(list, this); - } - - void IAddRangeProvider>.AddRangeTo(RecyclableLongList> list) - { - zRecyclableLongListAddRange.AddRange(list, this); - } } } diff --git a/Recyclable.Collections/RecyclableList.AddRange.cs b/Recyclable.Collections/RecyclableList.AddRange.cs index f12696f..b51d56b 100644 --- a/Recyclable.Collections/RecyclableList.AddRange.cs +++ b/Recyclable.Collections/RecyclableList.AddRange.cs @@ -1,12 +1,80 @@ 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 class AddRangeHelper + { + private static readonly Action, object>? _dictionaryAdder; + private static readonly Action, object>? _sortedListAdder; + private static readonly Type? _dictionaryType; + private static readonly Type? _sortedListType; + + static AddRangeHelper() + { + Type elementType = typeof(T); + if (elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)) + { + Type[] args = elementType.GetGenericArguments(); + _dictionaryType = typeof(RecyclableDictionary<,>).MakeGenericType(args); + MethodInfo? method = typeof(zRecyclableListAddRange).GetMethod( + nameof(AddRange), + BindingFlags.NonPublic | BindingFlags.Static, + binder: null, + types: new[] { typeof(RecyclableList<>).MakeGenericType(elementType), _dictionaryType }, + modifiers: null); + if (method != null) + { + var listParam = Expression.Parameter(typeof(RecyclableList)); + var objParam = Expression.Parameter(typeof(object)); + var call = Expression.Call(method, listParam, Expression.Convert(objParam, _dictionaryType)); + _dictionaryAdder = Expression.Lambda, object>>(call, listParam, objParam).Compile(); + } + } + else if (elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(ValueTuple<,>)) + { + Type[] args = elementType.GetGenericArguments(); + _sortedListType = typeof(RecyclableSortedList<,>).MakeGenericType(args); + MethodInfo? method = typeof(zRecyclableListAddRange).GetMethod( + nameof(AddRange), + BindingFlags.NonPublic | BindingFlags.Static, + binder: null, + types: new[] { typeof(RecyclableList<>).MakeGenericType(elementType), _sortedListType }, + modifiers: null); + if (method != null) + { + var listParam = Expression.Parameter(typeof(RecyclableList)); + var objParam = Expression.Parameter(typeof(object)); + var call = Expression.Call(method, listParam, Expression.Convert(objParam, _sortedListType)); + _sortedListAdder = Expression.Lambda, object>>(call, listParam, objParam).Compile(); + } + } + } + + internal static bool TryAddRange(RecyclableList list, IEnumerable items) + { + if (_dictionaryAdder != null && _dictionaryType!.IsInstanceOfType(items)) + { + _dictionaryAdder(list, items); + return true; + } + + if (_sortedListAdder != null && _sortedListType!.IsInstanceOfType(items)) + { + _sortedListAdder(list, items); + return true; + } + + return false; + } + } + private static void AddRangeEnumerated(this RecyclableList list, IEnumerable items, int growByCount) { var enumerator = items.GetEnumerator(); @@ -578,9 +646,8 @@ namespace Recyclable.Collections { AddRange(list, sourceQueue); } - else if (items is IAddRangeProvider provider) + else if (AddRangeHelper.TryAddRange(list, items)) { - provider.AddRangeTo(list); } else if (items is IReadOnlyList sourceIReadOnlyList) { diff --git a/Recyclable.Collections/RecyclableLongList.AddRange.cs b/Recyclable.Collections/RecyclableLongList.AddRange.cs index 825963b..cec751b 100644 --- a/Recyclable.Collections/RecyclableLongList.AddRange.cs +++ b/Recyclable.Collections/RecyclableLongList.AddRange.cs @@ -1,6 +1,8 @@ using System.Collections; using System.Numerics; using System.Runtime.CompilerServices; +using System.Linq.Expressions; +using System.Reflection; using Recyclable.Collections.Pools; namespace Recyclable.Collections @@ -8,6 +10,72 @@ namespace Recyclable.Collections public static class zRecyclableLongListAddRange { + private static class AddRangeHelper + { + private static readonly Action, object>? _dictionaryAdder; + private static readonly Action, object>? _sortedListAdder; + private static readonly Type? _dictionaryType; + private static readonly Type? _sortedListType; + + static AddRangeHelper() + { + Type elementType = typeof(T); + if (elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)) + { + Type[] args = elementType.GetGenericArguments(); + _dictionaryType = typeof(RecyclableDictionary<,>).MakeGenericType(args); + MethodInfo? method = typeof(zRecyclableLongListAddRange).GetMethod( + nameof(AddRange), + BindingFlags.NonPublic | BindingFlags.Static, + binder: null, + types: new[] { typeof(RecyclableLongList<>).MakeGenericType(elementType), _dictionaryType }, + modifiers: null); + if (method != null) + { + var listParam = Expression.Parameter(typeof(RecyclableLongList)); + var objParam = Expression.Parameter(typeof(object)); + var call = Expression.Call(method, listParam, Expression.Convert(objParam, _dictionaryType)); + _dictionaryAdder = Expression.Lambda, object>>(call, listParam, objParam).Compile(); + } + } + else if (elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(ValueTuple<,>)) + { + Type[] args = elementType.GetGenericArguments(); + _sortedListType = typeof(RecyclableSortedList<,>).MakeGenericType(args); + MethodInfo? method = typeof(zRecyclableLongListAddRange).GetMethod( + nameof(AddRange), + BindingFlags.NonPublic | BindingFlags.Static, + binder: null, + types: new[] { typeof(RecyclableLongList<>).MakeGenericType(elementType), _sortedListType }, + modifiers: null); + if (method != null) + { + var listParam = Expression.Parameter(typeof(RecyclableLongList)); + var objParam = Expression.Parameter(typeof(object)); + var call = Expression.Call(method, listParam, Expression.Convert(objParam, _sortedListType)); + _sortedListAdder = Expression.Lambda, object>>(call, listParam, objParam).Compile(); + } + } + } + + internal static bool TryAddRange(RecyclableLongList list, IEnumerable items) + { + if (_dictionaryAdder != null && _dictionaryType!.IsInstanceOfType(items)) + { + _dictionaryAdder(list, items); + return true; + } + + if (_sortedListAdder != null && _sortedListType!.IsInstanceOfType(items)) + { + _sortedListAdder(list, items); + return true; + } + + return false; + } + } + private static void AddRangeEnumerated(this RecyclableLongList targetList, IEnumerable items) { using IEnumerator enumerator = items.GetEnumerator(); @@ -439,23 +507,24 @@ namespace Recyclable.Collections targetList.AddRange(sourceQueue); return; - case IAddRangeProvider provider: - provider.AddRangeTo(targetList); - return; - default: + if (AddRangeHelper.TryAddRange(targetList, items)) + { + return; + } + if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity)) { targetList.AddRangeWithKnownCount(items, requiredAdditionalCapacity); - } - else - { - targetList.AddRangeEnumerated(items); - } + } + else + { + targetList.AddRangeEnumerated(items); + } - return; - } - } + return; + } + } public static void AddRange(this RecyclableLongList targetList, IReadOnlyList items) { diff --git a/Recyclable.Collections/RecyclableSortedList.cs b/Recyclable.Collections/RecyclableSortedList.cs index b11ca58..283b643 100644 --- a/Recyclable.Collections/RecyclableSortedList.cs +++ b/Recyclable.Collections/RecyclableSortedList.cs @@ -5,7 +5,7 @@ using Recyclable.Collections.Pools; namespace Recyclable.Collections { - internal sealed class RecyclableSortedList : IEnumerable<(TKey Key, TValue Value)>, IDisposable, IAddRangeProvider<(TKey Key, TValue Value)> + internal sealed class RecyclableSortedList : IEnumerable<(TKey Key, TValue Value)>, IDisposable where TKey : notnull { internal static readonly bool _needsClearing = !typeof(TKey).IsValueType || !typeof(TValue).IsValueType; @@ -277,14 +277,5 @@ namespace Recyclable.Collections } } - void IAddRangeProvider<(TKey Key, TValue Value)>.AddRangeTo(RecyclableList<(TKey Key, TValue Value)> list) - { - zRecyclableListAddRange.AddRange(list, this); - } - - void IAddRangeProvider<(TKey Key, TValue Value)>.AddRangeTo(RecyclableLongList<(TKey Key, TValue Value)> list) - { - zRecyclableLongListAddRange.AddRange(list, this); - } } }