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); - } } }