diff --git a/AyCode.Core.Serializers.Console/AyCode.Core.Serializers.Console.csproj b/AyCode.Core.Serializers.Console/AyCode.Core.Serializers.Console.csproj
new file mode 100644
index 0000000..acdcc68
--- /dev/null
+++ b/AyCode.Core.Serializers.Console/AyCode.Core.Serializers.Console.csproj
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+
+
+
diff --git a/AyCode.Core.Serializers.Console/Program.cs b/AyCode.Core.Serializers.Console/Program.cs
new file mode 100644
index 0000000..ca5abb4
--- /dev/null
+++ b/AyCode.Core.Serializers.Console/Program.cs
@@ -0,0 +1,195 @@
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using AyCode.Core.Serializers.Binaries;
+using AyCode.Core.Tests.TestModels;
+using MessagePack;
+using MessagePack.Resolvers;
+
+namespace AyCode.Core.Serializers.Console;
+
+///
+/// Console application for Performance Diagnostics profiling.
+/// Run with: Debug > Performance Profiler in Visual Studio
+///
+/// Usage:
+/// dotnet run -- serialize # Profile serialize only
+/// dotnet run -- deserialize # Profile deserialize only
+/// dotnet run -- all # Profile both (default)
+///
+public static class Program
+{
+ private const int WarmupIterations = 50;
+ private const int TestIterations = 5000;
+
+ // Keep references to prevent GC during profiling
+ private static TestOrder s_testOrder = null!;
+ private static byte[] s_acBinaryData = null!;
+ private static byte[] s_acBinaryNoRefData = null!;
+ private static byte[] s_msgPackData = null!;
+ private static AcBinarySerializerOptions s_acBinaryOptions = null!;
+ private static AcBinarySerializerOptions s_acBinaryNoRefOptions = null!;
+ private static MessagePackSerializerOptions s_msgPackOptions = null!;
+
+ public static void Main(string[] args)
+ {
+ var mode = args.Length > 0 ? args[0].ToLower() : "all";
+
+ System.Console.WriteLine("=".PadRight(60, '='));
+ System.Console.WriteLine($"AcBinary Performance Profiler - Mode: {mode}");
+ System.Console.WriteLine("=".PadRight(60, '='));
+
+ Setup();
+ Warmup();
+
+ System.Console.WriteLine($"\nRunning {TestIterations} iterations...\n");
+
+ var sw = Stopwatch.StartNew();
+
+ switch (mode)
+ {
+ case "serialize":
+ case "ser":
+ RunSerializeTests();
+ break;
+ case "deserialize":
+ case "des":
+ RunDeserializeTests();
+ break;
+ default:
+ RunSerializeTests();
+ RunDeserializeTests();
+ break;
+ }
+
+ sw.Stop();
+ System.Console.WriteLine($"\nTotal time: {sw.ElapsedMilliseconds:N0} ms");
+ System.Console.WriteLine("=".PadRight(60, '='));
+ }
+
+ private static void Setup()
+ {
+ System.Console.WriteLine("Creating test data...");
+ TestDataFactory.ResetIdCounter();
+ var sharedTag = TestDataFactory.CreateTag("SharedTag");
+ var sharedUser = TestDataFactory.CreateUser("shareduser");
+ var sharedMeta = TestDataFactory.CreateMetadata("shared", withChild: true);
+
+ s_testOrder = TestDataFactory.CreateOrder(
+ itemCount: 3,
+ palletsPerItem: 3,
+ measurementsPerPallet: 3,
+ pointsPerMeasurement: 4,
+ sharedTag: sharedTag,
+ sharedUser: sharedUser,
+ sharedMetadata: sharedMeta);
+
+ s_acBinaryOptions = AcBinarySerializerOptions.Default;
+ s_acBinaryNoRefOptions = AcBinarySerializerOptions.WithoutReferenceHandling();
+ s_msgPackOptions = ContractlessStandardResolver.Options.WithCompression(MessagePackCompression.None);
+
+ s_acBinaryData = AcBinarySerializer.Serialize(s_testOrder, s_acBinaryOptions);
+ s_acBinaryNoRefData = AcBinarySerializer.Serialize(s_testOrder, s_acBinaryNoRefOptions);
+ s_msgPackData = MessagePackSerializer.Serialize(s_testOrder, s_msgPackOptions);
+
+ System.Console.WriteLine($" AcBinary (WithRef): {s_acBinaryData.Length:N0} bytes");
+ System.Console.WriteLine($" AcBinary (NoRef): {s_acBinaryNoRefData.Length:N0} bytes");
+ System.Console.WriteLine($" MessagePack: {s_msgPackData.Length:N0} bytes");
+ }
+
+ private static void Warmup()
+ {
+ System.Console.WriteLine($"Warming up ({WarmupIterations} iterations)...");
+ for (int i = 0; i < WarmupIterations; i++)
+ {
+ DoSerializeAcBinary();
+ DoSerializeAcBinaryNoRef();
+ DoSerializeMsgPack();
+ DoDeserializeAcBinary();
+ DoDeserializeAcBinaryNoRef();
+ DoDeserializeMsgPack();
+ }
+ }
+
+ private static void RunSerializeTests()
+ {
+ System.Console.WriteLine("--- SERIALIZE ---");
+
+ var sw = Stopwatch.StartNew();
+ for (int i = 0; i < TestIterations; i++)
+ {
+ DoSerializeAcBinary();
+ }
+ sw.Stop();
+ System.Console.WriteLine($" AcBinary (WithRef): {sw.ElapsedMilliseconds,6:N0} ms");
+
+ sw.Restart();
+ for (int i = 0; i < TestIterations; i++)
+ {
+ DoSerializeAcBinaryNoRef();
+ }
+ sw.Stop();
+ System.Console.WriteLine($" AcBinary (NoRef): {sw.ElapsedMilliseconds,6:N0} ms");
+
+ sw.Restart();
+ for (int i = 0; i < TestIterations; i++)
+ {
+ DoSerializeMsgPack();
+ }
+ sw.Stop();
+ System.Console.WriteLine($" MessagePack: {sw.ElapsedMilliseconds,6:N0} ms");
+ }
+
+ private static void RunDeserializeTests()
+ {
+ System.Console.WriteLine("--- DESERIALIZE ---");
+
+ var sw = Stopwatch.StartNew();
+ for (int i = 0; i < TestIterations; i++)
+ {
+ DoDeserializeAcBinary();
+ }
+ sw.Stop();
+ System.Console.WriteLine($" AcBinary (WithRef): {sw.ElapsedMilliseconds,6:N0} ms");
+
+ sw.Restart();
+ for (int i = 0; i < TestIterations; i++)
+ {
+ DoDeserializeAcBinaryNoRef();
+ }
+ sw.Stop();
+ System.Console.WriteLine($" AcBinary (NoRef): {sw.ElapsedMilliseconds,6:N0} ms");
+
+ sw.Restart();
+ for (int i = 0; i < TestIterations; i++)
+ {
+ DoDeserializeMsgPack();
+ }
+ sw.Stop();
+ System.Console.WriteLine($" MessagePack: {sw.ElapsedMilliseconds,6:N0} ms");
+ }
+
+ // Separate methods for better profiler visibility - NO INLINING
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static byte[] DoSerializeAcBinary()
+ => AcBinarySerializer.Serialize(s_testOrder, s_acBinaryOptions);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static byte[] DoSerializeAcBinaryNoRef()
+ => AcBinarySerializer.Serialize(s_testOrder, s_acBinaryNoRefOptions);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static byte[] DoSerializeMsgPack()
+ => MessagePackSerializer.Serialize(s_testOrder, s_msgPackOptions);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static TestOrder? DoDeserializeAcBinary()
+ => AcBinaryDeserializer.Deserialize(s_acBinaryData);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static TestOrder? DoDeserializeAcBinaryNoRef()
+ => AcBinaryDeserializer.Deserialize(s_acBinaryNoRefData);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static TestOrder? DoDeserializeMsgPack()
+ => MessagePackSerializer.Deserialize(s_msgPackData, s_msgPackOptions);
+}
diff --git a/AyCode.Core.sln b/AyCode.Core.sln
index 02490a2..54c8305 100644
--- a/AyCode.Core.sln
+++ b/AyCode.Core.sln
@@ -53,6 +53,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AyCode.Services.Tests", "Ay
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AyCode.Core.Serializers.SourceGenerator", "AyCode.Core.Serializers.SourceGenerator\AyCode.Core.Serializers.SourceGenerator.csproj", "{4A817897-80A8-4F42-86C5-20447401E0AA}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AyCode.Core.Serializers.Console", "AyCode.Core.Serializers.Console\AyCode.Core.Serializers.Console.csproj", "{6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -433,6 +435,24 @@ Global
{4A817897-80A8-4F42-86C5-20447401E0AA}.Release|x64.Build.0 = Release|Any CPU
{4A817897-80A8-4F42-86C5-20447401E0AA}.Release|x86.ActiveCfg = Release|Any CPU
{4A817897-80A8-4F42-86C5-20447401E0AA}.Release|x86.Build.0 = Release|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Debug|x64.Build.0 = Debug|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Debug|x86.Build.0 = Debug|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Product|Any CPU.ActiveCfg = Release|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Product|Any CPU.Build.0 = Release|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Product|x64.ActiveCfg = Release|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Product|x64.Build.0 = Release|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Product|x86.ActiveCfg = Release|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Product|x86.Build.0 = Release|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Release|x64.ActiveCfg = Release|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Release|x64.Build.0 = Release|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Release|x86.ActiveCfg = Release|Any CPU
+ {6AB7CE43-3C98-1D54-9ABD-E5E9364541E7}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializeTypeMetadata.cs b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializeTypeMetadata.cs
index d7ca6dc..746c28c 100644
--- a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializeTypeMetadata.cs
+++ b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializeTypeMetadata.cs
@@ -151,7 +151,7 @@ public static partial class AcBinaryDeserializer
public new bool IsIIdCollection => _isManualConstruction ? _manualIsIIdCollection : base.IsIIdCollection;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public new object? GetValue(object target) => _isManualConstruction ? _manualGetter!(target) : base.GetDynamicValue(target);
+ public new object? GetValue(object target) => _isManualConstruction ? _manualGetter!(target) : base.GetValue(target);
public override void SetValue(object target, object? value)
{
diff --git a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.Populate.cs b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.Populate.cs
index 42bca2b..b134256 100644
--- a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.Populate.cs
+++ b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.Populate.cs
@@ -110,10 +110,10 @@ public static partial class AcBinaryDeserializer
var existingObj = propInfo.GetValue(target);
if (existingObj != null)
{
- var wrapper = context.ContextClass.GetWrapper(propInfo.PropertyType);
-
context.ReadByte(); // consume Object marker
+ var wrapper = context.ContextClass.GetWrapper(propInfo.PropertyType);
+
// Handle ref ID if present
if (context.HasReferenceHandling)
{
@@ -135,7 +135,9 @@ public static partial class AcBinaryDeserializer
try
{
// Use typed setters for primitives to avoid boxing
- if (TryReadAndSetTypedValue(ref context, target, propInfo, peekCode))
+ // Skip method call for Object/String/Collection types - they can't use typed setters
+ if (propInfo.AccessorType != PropertyAccessorType.Object &&
+ TryReadAndSetTypedValue(ref context, target, propInfo, peekCode))
continue;
var value = ReadValue(ref context, propInfo.PropertyType, nextDepth);
@@ -214,9 +216,16 @@ public static partial class AcBinaryDeserializer
try
{
- var wrapper = context.ContextClass.GetWrapper(elementType);
var existingCount = existingList.Count;
+ // Early exit if empty source - just clear destination
+ if (count == 0)
+ {
+ existingList.Clear();
+ return;
+ }
+
+ var wrapper = context.ContextClass.GetWrapper(elementType);
var elementMetadata = wrapper.Metadata.IsComplexType ? wrapper.Metadata : null;
for (int i = 0; i < count; i++)
diff --git a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs
index 01d1291..f4c9c53 100644
--- a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs
+++ b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs
@@ -89,6 +89,7 @@ public static partial class AcBinarySerializer
public bool UseMetadata { get; private set; }
public byte MaxDepth { get; private set; }
public byte MinStringInternLength { get; private set; }
+ public byte MaxStringInternLength { get; private set; }
public BinaryPropertyFilter? PropertyFilter { get; private set; }
public int Position => _position;
@@ -114,6 +115,7 @@ public static partial class AcBinarySerializer
UseMetadata = options.UseMetadata;
MaxDepth = options.MaxDepth;
MinStringInternLength = options.MinStringInternLength;
+ MaxStringInternLength = options.MaxStringInternLength;
PropertyFilter = options.PropertyFilter;
_initialBufferSize = Math.Max(options.InitialBufferCapacity, MinBufferSize);
diff --git a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.cs b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.cs
index d299588..1440cd6 100644
--- a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.cs
+++ b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.cs
@@ -1,4 +1,4 @@
-using AyCode.Core.Helpers;
+using AyCode.Core.Helpers;
using AyCode.Core.Serializers.Expressions;
using System.Buffers;
using System.Collections;
@@ -597,7 +597,11 @@ public static partial class AcBinarySerializer
return;
}
- if (context.UseStringInterning && value.Length >= context.MinStringInternLength)
+ // String interning: only for strings within length range
+ // MaxStringInternLength == 0 means no max limit
+ if (context.UseStringInterning
+ && value.Length >= context.MinStringInternLength
+ && (context.MaxStringInternLength == 0 || value.Length <= context.MaxStringInternLength))
{
var index = context.RegisterInternedString(value);
context.WriteByte(BinaryTypeCode.StringInterned);
@@ -642,12 +646,12 @@ public static partial class AcBinarySerializer
case AcSerializerCommon.IdAccessorType.Int32:
if (!context.TryTrack(wrapper, value, out int intId))
{
- // Already seen → write reference
+ // Already seen › write reference
context.WriteByte(BinaryTypeCode.ObjectRef);
context.WriteVarInt(intId);
return;
}
- // First occurrence → write object with refId
+ // First occurrence › write object with refId
context.WriteByte(BinaryTypeCode.Object);
context.WriteVarInt(intId);
break;
@@ -747,7 +751,7 @@ public static partial class AcBinarySerializer
return false;
default:
// Object type - use regular getter
- var value = prop.GetDynamicValue(obj);
+ var value = prop.GetValue(obj);
if (value == null) return true;
if (prop.PropertyTypeCode == TypeCode.String) return string.IsNullOrEmpty((string)value);
return false;
@@ -818,7 +822,7 @@ public static partial class AcBinarySerializer
return;
default:
// Fallback to object getter for reference types
- var value = prop.GetDynamicValue(obj);
+ var value = prop.GetValue(obj);
WriteValue(value, prop.PropertyType, context, depth);
return;
}
@@ -975,7 +979,7 @@ public static partial class AcBinarySerializer
default:
{
// Object type - use regular getter
- var value = prop.GetDynamicValue(obj);
+ var value = prop.GetValue(obj);
// SKIP marker only for null (reference types)
// Empty string, empty collections, etc. are valid values and must be written!
@@ -1211,4 +1215,4 @@ public static partial class AcBinarySerializer
// Type metadata helpers moved to AcBinarySerializer.BinarySerializeTypeMetadata.cs
#endregion
-}
\ No newline at end of file
+}
diff --git a/AyCode.Core/Serializers/Binaries/AcBinarySerializerOptions.cs b/AyCode.Core/Serializers/Binaries/AcBinarySerializerOptions.cs
index 0961010..038b33e 100644
--- a/AyCode.Core/Serializers/Binaries/AcBinarySerializerOptions.cs
+++ b/AyCode.Core/Serializers/Binaries/AcBinarySerializerOptions.cs
@@ -102,6 +102,14 @@ public sealed class AcBinarySerializerOptions : AcSerializerOptions
///
public byte MinStringInternLength { get; init; } = 4;
+ ///
+ /// Maximum string length to consider for interning.
+ /// Longer strings (descriptions, notes, etc.) are usually unique and not worth interning.
+ /// Set to 0 to disable max limit.
+ /// Default: 64 (strings longer than 64 chars are not interned)
+ ///
+ public byte MaxStringInternLength { get; init; } = 64;
+
///
/// Initial capacity for serialization buffer.
/// Default: 4096 bytes
diff --git a/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.JsonElement.cs b/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.JsonElement.cs
index 050aa2e..706fe3c 100644
--- a/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.JsonElement.cs
+++ b/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.JsonElement.cs
@@ -116,7 +116,7 @@ public static partial class AcJsonDeserializer
for (var i = 0; i < props.Length; i++)
{
var prop = props[i];
- var value = prop.GetDynamicValue(source);
+ var value = prop.GetValue(source);
if (value != null)
prop.SetValue(target, value);
}
@@ -176,7 +176,7 @@ public static partial class AcJsonDeserializer
// Handle IId collection merge
if (propInfo.IsIIdCollection && propValueKind == JsonValueKind.Array)
{
- var existingCollection = propInfo.GetDynamicValue(target);
+ var existingCollection = propInfo.GetValue(target);
if (existingCollection != null)
{
MergeIIdCollection(propValue, existingCollection, propInfo, context, depth);
@@ -201,7 +201,7 @@ public static partial class AcJsonDeserializer
// Merge into existing object
if (!propInfo.PropertyType.IsPrimitive && !ReferenceEquals(propInfo.PropertyType, StringType))
{
- var existingObj = propInfo.GetDynamicValue(target);
+ var existingObj = propInfo.GetValue(target);
if (existingObj != null)
{
var nestedMetadata = GetTypeMetadata(propInfo.PropertyType);
diff --git a/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.Utf8Reader.cs b/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.Utf8Reader.cs
index dd4b407..d2a4657 100644
--- a/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.Utf8Reader.cs
+++ b/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.Utf8Reader.cs
@@ -489,7 +489,7 @@ public static partial class AcJsonDeserializer
// Handle IId collection merge
if (propInfo.IsIIdCollection && tokenType == JsonTokenType.StartArray)
{
- var existingCollection = propInfo.GetDynamicValue(target);
+ var existingCollection = propInfo.GetValue(target);
if (existingCollection != null)
{
MergeIIdCollectionFromReader(ref reader, existingCollection, propInfo, maxDepth, depth);
@@ -500,7 +500,7 @@ public static partial class AcJsonDeserializer
// Handle nested objects - merge into existing
if (tokenType == JsonTokenType.StartObject && !propInfo.PropertyType.IsPrimitive && !ReferenceEquals(propInfo.PropertyType, StringType))
{
- var existingObj = propInfo.GetDynamicValue(target);
+ var existingObj = propInfo.GetValue(target);
if (existingObj != null)
{
var nestedMetadata = GetTypeMetadata(propInfo.PropertyType);
@@ -611,7 +611,7 @@ public static partial class AcJsonDeserializer
{
foreach (var prop in metadata.PropertySettersFrozen.Values)
{
- var value = prop.GetDynamicValue(source);
+ var value = prop.GetValue(source);
if (value != null)
prop.SetValue(target, value);
}
diff --git a/AyCode.Core/Serializers/Jsons/AcJsonSerializer.cs b/AyCode.Core/Serializers/Jsons/AcJsonSerializer.cs
index a16c277..02e1b4b 100644
--- a/AyCode.Core/Serializers/Jsons/AcJsonSerializer.cs
+++ b/AyCode.Core/Serializers/Jsons/AcJsonSerializer.cs
@@ -149,7 +149,7 @@ public static partial class AcJsonSerializer
var propCount = props.Length;
for (var i = 0; i < propCount; i++)
{
- var propValue = props[i].GetDynamicValue(value);
+ var propValue = props[i].GetValue(value);
if (propValue != null) ScanReferences(propValue, context, depth + 1);
}
}
@@ -205,7 +205,7 @@ public static partial class AcJsonSerializer
for (var i = 0; i < propCount; i++)
{
var prop = props[i];
- var propValue = prop.GetDynamicValue(value);
+ var propValue = prop.GetValue(value);
if (propValue == null) continue;
if (IsDefaultValueFast(propValue, prop.PropertyTypeCode, prop.PropertyType)) continue;
diff --git a/AyCode.Core/Serializers/PropertyAccessorBase.cs b/AyCode.Core/Serializers/PropertyAccessorBase.cs
index 59cea81..77a4f85 100644
--- a/AyCode.Core/Serializers/PropertyAccessorBase.cs
+++ b/AyCode.Core/Serializers/PropertyAccessorBase.cs
@@ -1,90 +1,16 @@
using System.Reflection;
using System.Runtime.CompilerServices;
-using System.Text;
using static AyCode.Core.Helpers.JsonUtilities;
namespace AyCode.Core.Serializers;
///
-/// Enum for typed property accessor dispatch.
-///
-public enum PropertyAccessorType : byte
-{
- Object = 0,
- Int32,
- Int64,
- Boolean,
- Double,
- Single,
- Decimal,
- DateTime,
- Byte,
- Int16,
- UInt16,
- UInt32,
- UInt64,
- Guid,
- Enum
-}
-
-///
-/// Base class for property accessors used by all serializers.
-/// Contains common property metadata, getter functionality, and typed delegate fields.
+/// Base class for property accessors used by serializers.
+/// Contains getter functionality and typed delegate fields.
/// Typed getters eliminate runtime cast overhead for value type properties.
///
-public abstract class PropertyAccessorBase
+public abstract class PropertyAccessorBase : PropertyMetadataBase
{
- ///
- /// Property name.
- ///
- public string Name { get; }
-
- ///
- /// Pre-encoded UTF8 bytes of property name for fast matching.
- ///
- public byte[] NameUtf8 { get; }
-
- ///
- /// The property type (may be nullable).
- ///
- public Type PropertyType { get; }
-
- ///
- /// The underlying type (unwrapped from Nullable if applicable).
- ///
- public Type UnderlyingType { get; }
-
- ///
- /// Cached TypeCode for fast primitive type dispatch.
- ///
- public TypeCode PropertyTypeCode { get; }
-
- ///
- /// Whether the property type is nullable.
- ///
- public bool IsNullable { get; }
-
- ///
- /// The declaring type of this property.
- ///
- public Type DeclaringType { get; }
-
- ///
- /// True if this property needs recursive scanning (not primitive/string).
- /// Pre-computed to avoid IsPrimitiveOrStringFast() calls in hot path.
- ///
- public bool IsComplexType { get; }
-
- ///
- /// The accessor type for fast typed getter dispatch.
- ///
- public PropertyAccessorType AccessorType { get; }
-
- ///
- /// Compiled getter delegate for reading property values (boxed).
- ///
- protected readonly Func