diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 2511057..b418660 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -28,7 +28,8 @@ "Bash(timeout 30 dotnet run:*)", "Bash(dotnet exec vstest:*)", "Bash(dotnet new:*)", - "Bash(Remove-Item \"H:\\\\Applications\\\\Aycode\\\\Source\\\\AyCode.Core\\\\AyCode.Core\\\\Serializers\\\\Toons\\\\AcToonSerializer.RelationshipDetection.cs\")" + "Bash(Remove-Item \"H:\\\\Applications\\\\Aycode\\\\Source\\\\AyCode.Core\\\\AyCode.Core\\\\Serializers\\\\Toons\\\\AcToonSerializer.RelationshipDetection.cs\")", + "Bash(find:*)" ] } } diff --git a/AyCode.Core/Helpers/JsonUtilities.cs b/AyCode.Core/Helpers/JsonUtilities.cs index 339abf3..a189358 100644 --- a/AyCode.Core/Helpers/JsonUtilities.cs +++ b/AyCode.Core/Helpers/JsonUtilities.cs @@ -97,7 +97,7 @@ public static class JsonUtilities private static readonly ConcurrentDictionary IsCollectionCache = new(); private static readonly ConcurrentDictionary IsPrimitiveCollectionCache = new(); private static readonly ConcurrentDictionary JsonIgnoreCache = new(); - private static readonly ConcurrentDictionary> ListFactoryCache = new(); + private static readonly ConcurrentDictionary> ListFactoryCache = new(); #endregion @@ -585,16 +585,32 @@ public static class JsonUtilities /// /// Gets or creates a list factory for a given element type. + /// Accepts capacity parameter: capacity <= 0 uses parameterless constructor, + /// capacity > 0 uses List<T>(capacity) constructor. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Func GetOrCreateListFactory(in Type elementType) + public static Func GetOrCreateListFactory(in Type elementType) { return ListFactoryCache.GetOrAdd(elementType, static t => { var listType = ListGenericType.MakeGenericType(t); - var newExpr = System.Linq.Expressions.Expression.New(listType); - var castExpr = System.Linq.Expressions.Expression.Convert(newExpr, typeof(IList)); - return System.Linq.Expressions.Expression.Lambda>(castExpr).Compile(); + var capacityParam = System.Linq.Expressions.Expression.Parameter(typeof(int), "capacity"); + + // new List() - parameterless + var defaultCtor = listType.GetConstructor(Type.EmptyTypes)!; + var newDefault = System.Linq.Expressions.Expression.New(defaultCtor); + + // new List(capacity) - with capacity + var capacityCtor = listType.GetConstructor(new[] { typeof(int) })!; + var newWithCapacity = System.Linq.Expressions.Expression.New(capacityCtor, capacityParam); + + // capacity > 0 ? new List(capacity) : new List() + var condition = System.Linq.Expressions.Expression.Condition( + System.Linq.Expressions.Expression.GreaterThan(capacityParam, System.Linq.Expressions.Expression.Constant(0)), + System.Linq.Expressions.Expression.Convert(newWithCapacity, typeof(IList)), + System.Linq.Expressions.Expression.Convert(newDefault, typeof(IList))); + + return System.Linq.Expressions.Expression.Lambda>(condition, capacityParam).Compile(); }); } diff --git a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.cs b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.cs index c5a2f98..3242161 100644 --- a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.cs +++ b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.cs @@ -1032,7 +1032,7 @@ public static partial class AcBinaryDeserializer /* Fallback to List */ } - list ??= GetOrCreateListFactory(elementType)(); + list ??= GetOrCreateListFactory(elementType)(count); var acObservable = list as IAcObservableCollection; acObservable?.BeginUpdate(); @@ -1189,9 +1189,8 @@ public static partial class AcBinaryDeserializer private static object ReadDictionaryAsObject(ref BinaryDeserializationContext context, Type keyType, Type valueType, int depth) { var dictType = DictionaryGenericType.MakeGenericType(keyType, valueType); - var dict = (IDictionary)Activator.CreateInstance(dictType)!; - var count = (int)context.ReadVarUInt(); + var dict = (IDictionary)Activator.CreateInstance(dictType, count)!; var nextDepth = depth + 1; for (int i = 0; i < count; i++) diff --git a/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.JsonElement.cs b/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.JsonElement.cs index 706fe3c..64087b3 100644 --- a/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.JsonElement.cs +++ b/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.JsonElement.cs @@ -281,7 +281,7 @@ public static partial class AcJsonDeserializer if (targetType.IsArray) { - var list = GetOrCreateListFactory(elementType)(); + var list = GetOrCreateListFactory(elementType)(0); foreach (var item in element.EnumerateArray()) list.Add(ReadValue(item, elementType, context, nextDepth)); @@ -298,7 +298,7 @@ public static partial class AcJsonDeserializer } catch { /* Fallback to List */ } - targetList ??= GetOrCreateListFactory(elementType)(); + targetList ??= GetOrCreateListFactory(elementType)(0); var acObservable = targetList as IAcObservableCollection; acObservable?.BeginUpdate(); diff --git a/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.Utf8Reader.cs b/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.Utf8Reader.cs index d2a4657..08a0c98 100644 --- a/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.Utf8Reader.cs +++ b/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.Utf8Reader.cs @@ -322,7 +322,7 @@ public static partial class AcJsonDeserializer if (elementType == null) return null; var nextDepth = depth + 1; - var list = GetOrCreateListFactory(elementType)(); + var list = GetOrCreateListFactory(elementType)(0); while (reader.Read()) {