Refactor: centralize strongly-typed property accessors
Move strongly-typed getter/setter logic and PropertyAccessorType enum into PropertyAccessorBase and PropertySetterBase, eliminating duplication in binary accessor classes. Expose direct typed getter/setter methods and new SetValueTyped/SetToDefault helpers. Rename ObjectGetter to DynamicGetter and update all serializers/deserializers to use GetDynamicValue. Centralize default value logic and improve performance by reducing boxing/unboxing. This unifies and streamlines property accessor infrastructure across all serializers.
This commit is contained in:
parent
8f35f172f0
commit
75823d593b
|
|
@ -236,7 +236,7 @@ public class AcExpressionNodeSerializationTests
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region SetValue/GetValue Tests
|
#region SetValue/GetDynamicValue Tests
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[DataRow(42, ConstantValueType.Int32)]
|
[DataRow(42, ConstantValueType.Int32)]
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ public static partial class AcBinaryDeserializer
|
||||||
public new bool IsIIdCollection => _isManualConstruction ? _manualIsIIdCollection : base.IsIIdCollection;
|
public new bool IsIIdCollection => _isManualConstruction ? _manualIsIIdCollection : base.IsIIdCollection;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public new object? GetValue(object target) => _isManualConstruction ? _manualGetter!(target) : base.GetValue(target);
|
public new object? GetValue(object target) => _isManualConstruction ? _manualGetter!(target) : base.GetDynamicValue(target);
|
||||||
|
|
||||||
public override void SetValue(object target, object? value)
|
public override void SetValue(object target, object? value)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -515,43 +515,7 @@ public static partial class AcBinaryDeserializer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static void SetPropertyToDefault(object target, BinaryPropertySetterInfo propInfo)
|
private static void SetPropertyToDefault(object target, BinaryPropertySetterInfo propInfo)
|
||||||
{
|
=> propInfo.SetToDefault(target);
|
||||||
switch (propInfo.SetterType)
|
|
||||||
{
|
|
||||||
case PropertyAccessorType.Int32:
|
|
||||||
propInfo.SetInt32(target, 0);
|
|
||||||
return;
|
|
||||||
case PropertyAccessorType.Int64:
|
|
||||||
propInfo.SetInt64(target, 0L);
|
|
||||||
return;
|
|
||||||
case PropertyAccessorType.Boolean:
|
|
||||||
propInfo.SetBoolean(target, false);
|
|
||||||
return;
|
|
||||||
case PropertyAccessorType.Double:
|
|
||||||
propInfo.SetDouble(target, 0.0);
|
|
||||||
return;
|
|
||||||
case PropertyAccessorType.Single:
|
|
||||||
propInfo.SetSingle(target, 0f);
|
|
||||||
return;
|
|
||||||
case PropertyAccessorType.Decimal:
|
|
||||||
propInfo.SetDecimal(target, 0m);
|
|
||||||
return;
|
|
||||||
case PropertyAccessorType.DateTime:
|
|
||||||
propInfo.SetDateTime(target, default);
|
|
||||||
return;
|
|
||||||
case PropertyAccessorType.Guid:
|
|
||||||
propInfo.SetGuid(target, default);
|
|
||||||
return;
|
|
||||||
case PropertyAccessorType.Enum:
|
|
||||||
propInfo.SetEnumAsInt32(target, 0);
|
|
||||||
return;
|
|
||||||
case PropertyAccessorType.Object:
|
|
||||||
default:
|
|
||||||
// Reference types and nullable value types: set to null
|
|
||||||
propInfo.SetValue(target, null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines if a type is a complex type (not primitive, string, or simple value type).
|
/// Determines if a type is a complex type (not primitive, string, or simple value type).
|
||||||
|
|
@ -573,3 +537,4 @@ public static partial class AcBinaryDeserializer
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -510,11 +510,11 @@ public static partial class AcBinaryDeserializer
|
||||||
private static bool TryReadAndSetTypedValue(ref BinaryDeserializationContext context, object target, BinaryPropertySetterInfo propInfo, byte peekCode)
|
private static bool TryReadAndSetTypedValue(ref BinaryDeserializationContext context, object target, BinaryPropertySetterInfo propInfo, byte peekCode)
|
||||||
{
|
{
|
||||||
// Only handle if we have a typed setter
|
// Only handle if we have a typed setter
|
||||||
if (propInfo.SetterType == PropertyAccessorType.Object)
|
if (propInfo.AccessorType == PropertyAccessorType.Object)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Handle based on property setter type and incoming data type
|
// Handle based on property setter type and incoming data type
|
||||||
switch (propInfo.SetterType)
|
switch (propInfo.AccessorType)
|
||||||
{
|
{
|
||||||
case PropertyAccessorType.Int32:
|
case PropertyAccessorType.Int32:
|
||||||
if (BinaryTypeCode.IsTinyInt(peekCode))
|
if (BinaryTypeCode.IsTinyInt(peekCode))
|
||||||
|
|
@ -1474,3 +1474,4 @@ public static partial class AcBinaryDeserializer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementation moved to AcBinaryDeserializer.TypeConversionInfo.cs
|
// Implementation moved to AcBinaryDeserializer.TypeConversionInfo.cs
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -430,7 +430,7 @@ public static partial class AcBinarySerializer
|
||||||
property.DeclaringType,
|
property.DeclaringType,
|
||||||
property.Name,
|
property.Name,
|
||||||
property.PropertyType,
|
property.PropertyType,
|
||||||
property.ObjectGetter);
|
property.DynamicGetter);
|
||||||
return PropertyFilter(context);
|
return PropertyFilter(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -747,7 +747,7 @@ public static partial class AcBinarySerializer
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
// Object type - use regular getter
|
// Object type - use regular getter
|
||||||
var value = prop.GetValue(obj);
|
var value = prop.GetDynamicValue(obj);
|
||||||
if (value == null) return true;
|
if (value == null) return true;
|
||||||
if (prop.PropertyTypeCode == TypeCode.String) return string.IsNullOrEmpty((string)value);
|
if (prop.PropertyTypeCode == TypeCode.String) return string.IsNullOrEmpty((string)value);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -818,7 +818,7 @@ public static partial class AcBinarySerializer
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
// Fallback to object getter for reference types
|
// Fallback to object getter for reference types
|
||||||
var value = prop.GetValue(obj);
|
var value = prop.GetDynamicValue(obj);
|
||||||
WriteValue(value, prop.PropertyType, context, depth);
|
WriteValue(value, prop.PropertyType, context, depth);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -975,7 +975,7 @@ public static partial class AcBinarySerializer
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Object type - use regular getter
|
// Object type - use regular getter
|
||||||
var value = prop.GetValue(obj);
|
var value = prop.GetDynamicValue(obj);
|
||||||
|
|
||||||
// SKIP marker only for null (reference types)
|
// SKIP marker only for null (reference types)
|
||||||
// Empty string, empty collections, etc. are valid values and must be written!
|
// Empty string, empty collections, etc. are valid values and must be written!
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@ using static AyCode.Core.Helpers.JsonUtilities;
|
||||||
namespace AyCode.Core.Serializers.Binaries;
|
namespace AyCode.Core.Serializers.Binaries;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Binary-specific property accessor base class.
|
/// Binary-specific property accessor.
|
||||||
/// Adds typed getters to avoid boxing during serialization.
|
/// Inherits typed getters from PropertyAccessorBase.
|
||||||
|
/// Adds Binary-specific properties: PropertyIndex, CachedPropertyNameIndex.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class BinaryPropertyAccessorBase : PropertyAccessorBase
|
public abstract class BinaryPropertyAccessorBase : PropertyAccessorBase
|
||||||
{
|
{
|
||||||
|
|
@ -23,130 +24,14 @@ public abstract class BinaryPropertyAccessorBase : PropertyAccessorBase
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int PropertyIndex { get; internal set; } = -1;
|
public int PropertyIndex { get; internal set; } = -1;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The accessor type for fast typed getter dispatch.
|
|
||||||
/// </summary>
|
|
||||||
public PropertyAccessorType AccessorType { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Typed getter delegate (type depends on AccessorType).
|
|
||||||
/// </summary>
|
|
||||||
protected readonly Delegate? _typedGetter;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Object getter for property filter context.
|
/// Object getter for property filter context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<object, object?> ObjectGetter => _getter;
|
public Func<object, object?> DynamicGetter => _dynamicGetter;
|
||||||
|
|
||||||
protected BinaryPropertyAccessorBase(PropertyInfo prop, Type declaringType)
|
protected BinaryPropertyAccessorBase(PropertyInfo prop, Type declaringType)
|
||||||
: base(prop, declaringType)
|
: base(prop, declaringType)
|
||||||
{
|
{
|
||||||
(_typedGetter, AccessorType) = CreateTypedGetterForAccessor(declaringType, prop);
|
// All typed getters are initialized in PropertyAccessorBase
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (Delegate?, PropertyAccessorType) CreateTypedGetterForAccessor(Type declaringType, PropertyInfo prop)
|
|
||||||
{
|
|
||||||
var propType = prop.PropertyType;
|
|
||||||
var underlying = Nullable.GetUnderlyingType(propType);
|
|
||||||
if (underlying != null)
|
|
||||||
{
|
|
||||||
return (null, PropertyAccessorType.Object);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (propType.IsEnum)
|
|
||||||
{
|
|
||||||
return (AcSerializerCommon.CreateEnumGetter(declaringType, prop), PropertyAccessorType.Enum);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ReferenceEquals(propType, GuidType))
|
|
||||||
{
|
|
||||||
return (AcSerializerCommon.CreateTypedGetter<Guid>(declaringType, prop), PropertyAccessorType.Guid);
|
|
||||||
}
|
|
||||||
|
|
||||||
var typeCode = Type.GetTypeCode(propType);
|
|
||||||
return typeCode switch
|
|
||||||
{
|
|
||||||
TypeCode.Int32 => (AcSerializerCommon.CreateTypedGetter<int>(declaringType, prop), PropertyAccessorType.Int32),
|
|
||||||
TypeCode.Int64 => (AcSerializerCommon.CreateTypedGetter<long>(declaringType, prop), PropertyAccessorType.Int64),
|
|
||||||
TypeCode.Boolean => (AcSerializerCommon.CreateTypedGetter<bool>(declaringType, prop), PropertyAccessorType.Boolean),
|
|
||||||
TypeCode.Double => (AcSerializerCommon.CreateTypedGetter<double>(declaringType, prop), PropertyAccessorType.Double),
|
|
||||||
TypeCode.Single => (AcSerializerCommon.CreateTypedGetter<float>(declaringType, prop), PropertyAccessorType.Single),
|
|
||||||
TypeCode.Decimal => (AcSerializerCommon.CreateTypedGetter<decimal>(declaringType, prop), PropertyAccessorType.Decimal),
|
|
||||||
TypeCode.DateTime => (AcSerializerCommon.CreateTypedGetter<DateTime>(declaringType, prop), PropertyAccessorType.DateTime),
|
|
||||||
TypeCode.Byte => (AcSerializerCommon.CreateTypedGetter<byte>(declaringType, prop), PropertyAccessorType.Byte),
|
|
||||||
TypeCode.Int16 => (AcSerializerCommon.CreateTypedGetter<short>(declaringType, prop), PropertyAccessorType.Int16),
|
|
||||||
TypeCode.UInt16 => (AcSerializerCommon.CreateTypedGetter<ushort>(declaringType, prop), PropertyAccessorType.UInt16),
|
|
||||||
TypeCode.UInt32 => (AcSerializerCommon.CreateTypedGetter<uint>(declaringType, prop), PropertyAccessorType.UInt32),
|
|
||||||
TypeCode.UInt64 => (AcSerializerCommon.CreateTypedGetter<ulong>(declaringType, prop), PropertyAccessorType.UInt64),
|
|
||||||
_ => (null, PropertyAccessorType.Object)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Typed Getters
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public int GetInt32(object obj) => ((Func<object, int>)_typedGetter!)(obj);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public long GetInt64(object obj) => ((Func<object, long>)_typedGetter!)(obj);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool GetBoolean(object obj) => ((Func<object, bool>)_typedGetter!)(obj);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public double GetDouble(object obj) => ((Func<object, double>)_typedGetter!)(obj);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public float GetSingle(object obj) => ((Func<object, float>)_typedGetter!)(obj);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public decimal GetDecimal(object obj) => ((Func<object, decimal>)_typedGetter!)(obj);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public DateTime GetDateTime(object obj) => ((Func<object, DateTime>)_typedGetter!)(obj);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public byte GetByte(object obj) => ((Func<object, byte>)_typedGetter!)(obj);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public short GetInt16(object obj) => ((Func<object, short>)_typedGetter!)(obj);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ushort GetUInt16(object obj) => ((Func<object, ushort>)_typedGetter!)(obj);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public uint GetUInt32(object obj) => ((Func<object, uint>)_typedGetter!)(obj);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ulong GetUInt64(object obj) => ((Func<object, ulong>)_typedGetter!)(obj);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public Guid GetGuid(object obj) => ((Func<object, Guid>)_typedGetter!)(obj);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public int GetEnumAsInt32(object obj) => ((Func<object, int>)_typedGetter!)(obj);
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Enum for typed property accessor dispatch.
|
|
||||||
/// </summary>
|
|
||||||
public enum PropertyAccessorType : byte
|
|
||||||
{
|
|
||||||
Object = 0,
|
|
||||||
Int32,
|
|
||||||
Int64,
|
|
||||||
Boolean,
|
|
||||||
Double,
|
|
||||||
Single,
|
|
||||||
Decimal,
|
|
||||||
DateTime,
|
|
||||||
Byte,
|
|
||||||
Int16,
|
|
||||||
UInt16,
|
|
||||||
UInt32,
|
|
||||||
UInt64,
|
|
||||||
Guid,
|
|
||||||
Enum
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,124 +7,29 @@ namespace AyCode.Core.Serializers.Binaries;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Binary-specific property setter base class.
|
/// Binary-specific property setter base class.
|
||||||
/// Extends PropertySetterBase with binary-specific functionality and typed setters.
|
/// Extends PropertySetterBase with binary-specific functionality.
|
||||||
|
/// Typed setters are inherited from PropertySetterBase.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class BinaryPropertySetterBase : PropertySetterBase
|
public abstract class BinaryPropertySetterBase : PropertySetterBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this property is a complex type (not primitive, string, enum, or common value types).
|
/// Whether this property is a complex type (not primitive, string, enum, or common value types).
|
||||||
|
/// Note: Shadows PropertyAccessorBase.IsComplexType with Binary-specific check.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsComplexType { get; }
|
public new bool IsComplexType { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this property is a collection type.
|
/// Whether this property is a collection type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsCollection { get; }
|
public bool IsCollection { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The setter type for fast typed setter dispatch.
|
|
||||||
/// </summary>
|
|
||||||
public PropertyAccessorType SetterType { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Typed setter delegate (type depends on SetterType).
|
|
||||||
/// </summary>
|
|
||||||
protected readonly Delegate? _typedSetter;
|
|
||||||
|
|
||||||
protected BinaryPropertySetterBase(PropertyInfo prop, Type declaringType)
|
protected BinaryPropertySetterBase(PropertyInfo prop, Type declaringType)
|
||||||
: base(prop, declaringType)
|
: base(prop, declaringType)
|
||||||
{
|
{
|
||||||
IsCollection = IsCollectionTypeCheck(PropertyType);
|
IsCollection = IsCollectionTypeCheck(PropertyType);
|
||||||
IsComplexType = IsComplex(PropertyType);
|
IsComplexType = IsComplex(PropertyType);
|
||||||
(_typedSetter, SetterType) = CreateTypedSetterForAccessor(declaringType, prop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (Delegate?, PropertyAccessorType) CreateTypedSetterForAccessor(Type declaringType, PropertyInfo prop)
|
|
||||||
{
|
|
||||||
var propType = prop.PropertyType;
|
|
||||||
var underlying = Nullable.GetUnderlyingType(propType);
|
|
||||||
if (underlying != null)
|
|
||||||
{
|
|
||||||
// Nullable types use Object path
|
|
||||||
return (null, PropertyAccessorType.Object);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (propType.IsEnum)
|
|
||||||
{
|
|
||||||
return (AcSerializerCommon.CreateEnumSetter(declaringType, prop), PropertyAccessorType.Enum);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ReferenceEquals(propType, GuidType))
|
|
||||||
{
|
|
||||||
return (AcSerializerCommon.CreateTypedSetter<Guid>(declaringType, prop), PropertyAccessorType.Guid);
|
|
||||||
}
|
|
||||||
|
|
||||||
var typeCode = Type.GetTypeCode(propType);
|
|
||||||
return typeCode switch
|
|
||||||
{
|
|
||||||
TypeCode.Int32 => (AcSerializerCommon.CreateTypedSetter<int>(declaringType, prop), PropertyAccessorType.Int32),
|
|
||||||
TypeCode.Int64 => (AcSerializerCommon.CreateTypedSetter<long>(declaringType, prop), PropertyAccessorType.Int64),
|
|
||||||
TypeCode.Boolean => (AcSerializerCommon.CreateTypedSetter<bool>(declaringType, prop), PropertyAccessorType.Boolean),
|
|
||||||
TypeCode.Double => (AcSerializerCommon.CreateTypedSetter<double>(declaringType, prop), PropertyAccessorType.Double),
|
|
||||||
TypeCode.Single => (AcSerializerCommon.CreateTypedSetter<float>(declaringType, prop), PropertyAccessorType.Single),
|
|
||||||
TypeCode.Decimal => (AcSerializerCommon.CreateTypedSetter<decimal>(declaringType, prop), PropertyAccessorType.Decimal),
|
|
||||||
TypeCode.DateTime => (AcSerializerCommon.CreateTypedSetter<DateTime>(declaringType, prop), PropertyAccessorType.DateTime),
|
|
||||||
TypeCode.Byte => (AcSerializerCommon.CreateTypedSetter<byte>(declaringType, prop), PropertyAccessorType.Byte),
|
|
||||||
TypeCode.Int16 => (AcSerializerCommon.CreateTypedSetter<short>(declaringType, prop), PropertyAccessorType.Int16),
|
|
||||||
TypeCode.UInt16 => (AcSerializerCommon.CreateTypedSetter<ushort>(declaringType, prop), PropertyAccessorType.UInt16),
|
|
||||||
TypeCode.UInt32 => (AcSerializerCommon.CreateTypedSetter<uint>(declaringType, prop), PropertyAccessorType.UInt32),
|
|
||||||
TypeCode.UInt64 => (AcSerializerCommon.CreateTypedSetter<ulong>(declaringType, prop), PropertyAccessorType.UInt64),
|
|
||||||
TypeCode.String => (null, PropertyAccessorType.Object), // String doesn't benefit from typed setter
|
|
||||||
_ => (null, PropertyAccessorType.Object)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Typed Setters
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void SetInt32(object obj, int value) => ((Action<object, int>)_typedSetter!)(obj, value);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void SetInt64(object obj, long value) => ((Action<object, long>)_typedSetter!)(obj, value);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void SetBoolean(object obj, bool value) => ((Action<object, bool>)_typedSetter!)(obj, value);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void SetDouble(object obj, double value) => ((Action<object, double>)_typedSetter!)(obj, value);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void SetSingle(object obj, float value) => ((Action<object, float>)_typedSetter!)(obj, value);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void SetDecimal(object obj, decimal value) => ((Action<object, decimal>)_typedSetter!)(obj, value);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void SetDateTime(object obj, DateTime value) => ((Action<object, DateTime>)_typedSetter!)(obj, value);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void SetByte(object obj, byte value) => ((Action<object, byte>)_typedSetter!)(obj, value);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void SetInt16(object obj, short value) => ((Action<object, short>)_typedSetter!)(obj, value);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void SetUInt16(object obj, ushort value) => ((Action<object, ushort>)_typedSetter!)(obj, value);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void SetUInt32(object obj, uint value) => ((Action<object, uint>)_typedSetter!)(obj, value);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void SetUInt64(object obj, ulong value) => ((Action<object, ulong>)_typedSetter!)(obj, value);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void SetGuid(object obj, Guid value) => ((Action<object, Guid>)_typedSetter!)(obj, value);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void SetEnumAsInt32(object obj, int value) => ((Action<object, int>)_typedSetter!)(obj, value);
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public override void SetValue(object target, object? value)
|
public override void SetValue(object target, object? value)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ public class AcExpressionRebuilder
|
||||||
{
|
{
|
||||||
var type = ResolveType(node.TypeName ?? "System.Object");
|
var type = ResolveType(node.TypeName ?? "System.Object");
|
||||||
|
|
||||||
// Use the type-safe GetValue method
|
// Use the type-safe GetDynamicValue method
|
||||||
var value = node.GetValue();
|
var value = node.GetValue();
|
||||||
|
|
||||||
if (value == null)
|
if (value == null)
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ public static partial class AcJsonDeserializer
|
||||||
for (var i = 0; i < props.Length; i++)
|
for (var i = 0; i < props.Length; i++)
|
||||||
{
|
{
|
||||||
var prop = props[i];
|
var prop = props[i];
|
||||||
var value = prop.GetValue(source);
|
var value = prop.GetDynamicValue(source);
|
||||||
if (value != null)
|
if (value != null)
|
||||||
prop.SetValue(target, value);
|
prop.SetValue(target, value);
|
||||||
}
|
}
|
||||||
|
|
@ -176,7 +176,7 @@ public static partial class AcJsonDeserializer
|
||||||
// Handle IId collection merge
|
// Handle IId collection merge
|
||||||
if (propInfo.IsIIdCollection && propValueKind == JsonValueKind.Array)
|
if (propInfo.IsIIdCollection && propValueKind == JsonValueKind.Array)
|
||||||
{
|
{
|
||||||
var existingCollection = propInfo.GetValue(target);
|
var existingCollection = propInfo.GetDynamicValue(target);
|
||||||
if (existingCollection != null)
|
if (existingCollection != null)
|
||||||
{
|
{
|
||||||
MergeIIdCollection(propValue, existingCollection, propInfo, context, depth);
|
MergeIIdCollection(propValue, existingCollection, propInfo, context, depth);
|
||||||
|
|
@ -201,7 +201,7 @@ public static partial class AcJsonDeserializer
|
||||||
// Merge into existing object
|
// Merge into existing object
|
||||||
if (!propInfo.PropertyType.IsPrimitive && !ReferenceEquals(propInfo.PropertyType, StringType))
|
if (!propInfo.PropertyType.IsPrimitive && !ReferenceEquals(propInfo.PropertyType, StringType))
|
||||||
{
|
{
|
||||||
var existingObj = propInfo.GetValue(target);
|
var existingObj = propInfo.GetDynamicValue(target);
|
||||||
if (existingObj != null)
|
if (existingObj != null)
|
||||||
{
|
{
|
||||||
var nestedMetadata = GetTypeMetadata(propInfo.PropertyType);
|
var nestedMetadata = GetTypeMetadata(propInfo.PropertyType);
|
||||||
|
|
|
||||||
|
|
@ -489,7 +489,7 @@ public static partial class AcJsonDeserializer
|
||||||
// Handle IId collection merge
|
// Handle IId collection merge
|
||||||
if (propInfo.IsIIdCollection && tokenType == JsonTokenType.StartArray)
|
if (propInfo.IsIIdCollection && tokenType == JsonTokenType.StartArray)
|
||||||
{
|
{
|
||||||
var existingCollection = propInfo.GetValue(target);
|
var existingCollection = propInfo.GetDynamicValue(target);
|
||||||
if (existingCollection != null)
|
if (existingCollection != null)
|
||||||
{
|
{
|
||||||
MergeIIdCollectionFromReader(ref reader, existingCollection, propInfo, maxDepth, depth);
|
MergeIIdCollectionFromReader(ref reader, existingCollection, propInfo, maxDepth, depth);
|
||||||
|
|
@ -500,7 +500,7 @@ public static partial class AcJsonDeserializer
|
||||||
// Handle nested objects - merge into existing
|
// Handle nested objects - merge into existing
|
||||||
if (tokenType == JsonTokenType.StartObject && !propInfo.PropertyType.IsPrimitive && !ReferenceEquals(propInfo.PropertyType, StringType))
|
if (tokenType == JsonTokenType.StartObject && !propInfo.PropertyType.IsPrimitive && !ReferenceEquals(propInfo.PropertyType, StringType))
|
||||||
{
|
{
|
||||||
var existingObj = propInfo.GetValue(target);
|
var existingObj = propInfo.GetDynamicValue(target);
|
||||||
if (existingObj != null)
|
if (existingObj != null)
|
||||||
{
|
{
|
||||||
var nestedMetadata = GetTypeMetadata(propInfo.PropertyType);
|
var nestedMetadata = GetTypeMetadata(propInfo.PropertyType);
|
||||||
|
|
@ -611,7 +611,7 @@ public static partial class AcJsonDeserializer
|
||||||
{
|
{
|
||||||
foreach (var prop in metadata.PropertySettersFrozen.Values)
|
foreach (var prop in metadata.PropertySettersFrozen.Values)
|
||||||
{
|
{
|
||||||
var value = prop.GetValue(source);
|
var value = prop.GetDynamicValue(source);
|
||||||
if (value != null)
|
if (value != null)
|
||||||
prop.SetValue(target, value);
|
prop.SetValue(target, value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ public static partial class AcJsonSerializer
|
||||||
var propCount = props.Length;
|
var propCount = props.Length;
|
||||||
for (var i = 0; i < propCount; i++)
|
for (var i = 0; i < propCount; i++)
|
||||||
{
|
{
|
||||||
var propValue = props[i].GetValue(value);
|
var propValue = props[i].GetDynamicValue(value);
|
||||||
if (propValue != null) ScanReferences(propValue, context, depth + 1);
|
if (propValue != null) ScanReferences(propValue, context, depth + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -205,7 +205,7 @@ public static partial class AcJsonSerializer
|
||||||
for (var i = 0; i < propCount; i++)
|
for (var i = 0; i < propCount; i++)
|
||||||
{
|
{
|
||||||
var prop = props[i];
|
var prop = props[i];
|
||||||
var propValue = prop.GetValue(value);
|
var propValue = prop.GetDynamicValue(value);
|
||||||
if (propValue == null) continue;
|
if (propValue == null) continue;
|
||||||
if (IsDefaultValueFast(propValue, prop.PropertyTypeCode, prop.PropertyType)) continue;
|
if (IsDefaultValueFast(propValue, prop.PropertyTypeCode, prop.PropertyType)) continue;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,32 @@ using static AyCode.Core.Helpers.JsonUtilities;
|
||||||
|
|
||||||
namespace AyCode.Core.Serializers;
|
namespace AyCode.Core.Serializers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enum for typed property accessor dispatch.
|
||||||
|
/// </summary>
|
||||||
|
public enum PropertyAccessorType : byte
|
||||||
|
{
|
||||||
|
Object = 0,
|
||||||
|
Int32,
|
||||||
|
Int64,
|
||||||
|
Boolean,
|
||||||
|
Double,
|
||||||
|
Single,
|
||||||
|
Decimal,
|
||||||
|
DateTime,
|
||||||
|
Byte,
|
||||||
|
Int16,
|
||||||
|
UInt16,
|
||||||
|
UInt32,
|
||||||
|
UInt64,
|
||||||
|
Guid,
|
||||||
|
Enum
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base class for property accessors used by all serializers.
|
/// Base class for property accessors used by all serializers.
|
||||||
/// Contains common property metadata and getter functionality.
|
/// Contains common property metadata, getter functionality, and typed delegate fields.
|
||||||
|
/// Typed getters eliminate runtime cast overhead for value type properties.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class PropertyAccessorBase
|
public abstract class PropertyAccessorBase
|
||||||
{
|
{
|
||||||
|
|
@ -53,9 +76,33 @@ public abstract class PropertyAccessorBase
|
||||||
public bool IsComplexType { get; }
|
public bool IsComplexType { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compiled getter delegate for reading property values.
|
/// The accessor type for fast typed getter dispatch.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly Func<object, object?> _getter;
|
public PropertyAccessorType AccessorType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compiled getter delegate for reading property values (boxed).
|
||||||
|
/// </summary>
|
||||||
|
protected readonly Func<object, object?> _dynamicGetter;
|
||||||
|
|
||||||
|
#region Strongly-typed getter delegate fields (eliminates runtime cast)
|
||||||
|
|
||||||
|
// Only ONE of these is set based on AccessorType
|
||||||
|
private readonly Func<object, int>? _int32Getter;
|
||||||
|
private readonly Func<object, long>? _int64Getter;
|
||||||
|
private readonly Func<object, bool>? _boolGetter;
|
||||||
|
private readonly Func<object, double>? _doubleGetter;
|
||||||
|
private readonly Func<object, float>? _floatGetter;
|
||||||
|
private readonly Func<object, decimal>? _decimalGetter;
|
||||||
|
private readonly Func<object, DateTime>? _dateTimeGetter;
|
||||||
|
private readonly Func<object, byte>? _byteGetter;
|
||||||
|
private readonly Func<object, short>? _int16Getter;
|
||||||
|
private readonly Func<object, ushort>? _uint16Getter;
|
||||||
|
private readonly Func<object, uint>? _uint32Getter;
|
||||||
|
private readonly Func<object, ulong>? _uint64Getter;
|
||||||
|
private readonly Func<object, Guid>? _guidGetter;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
protected PropertyAccessorBase(PropertyInfo prop, Type declaringType)
|
protected PropertyAccessorBase(PropertyInfo prop, Type declaringType)
|
||||||
{
|
{
|
||||||
|
|
@ -72,12 +119,141 @@ public abstract class PropertyAccessorBase
|
||||||
// Pre-compute: is this a complex type that needs recursive handling?
|
// Pre-compute: is this a complex type that needs recursive handling?
|
||||||
IsComplexType = !IsPrimitiveOrStringFast(PropertyType);
|
IsComplexType = !IsPrimitiveOrStringFast(PropertyType);
|
||||||
|
|
||||||
_getter = AcSerializerCommon.CreateCompiledGetter(declaringType, prop);
|
_dynamicGetter = AcSerializerCommon.CreateCompiledGetter(declaringType, prop);
|
||||||
|
|
||||||
|
// Initialize typed getter
|
||||||
|
AccessorType = DetermineAccessorType(PropertyType);
|
||||||
|
InitializeTypedGetter(declaringType, prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static PropertyAccessorType DetermineAccessorType(Type propType)
|
||||||
|
{
|
||||||
|
var underlying = Nullable.GetUnderlyingType(propType);
|
||||||
|
if (underlying != null)
|
||||||
|
return PropertyAccessorType.Object;
|
||||||
|
|
||||||
|
if (propType.IsEnum)
|
||||||
|
return PropertyAccessorType.Enum;
|
||||||
|
|
||||||
|
if (ReferenceEquals(propType, GuidType))
|
||||||
|
return PropertyAccessorType.Guid;
|
||||||
|
|
||||||
|
return Type.GetTypeCode(propType) switch
|
||||||
|
{
|
||||||
|
TypeCode.Int32 => PropertyAccessorType.Int32,
|
||||||
|
TypeCode.Int64 => PropertyAccessorType.Int64,
|
||||||
|
TypeCode.Boolean => PropertyAccessorType.Boolean,
|
||||||
|
TypeCode.Double => PropertyAccessorType.Double,
|
||||||
|
TypeCode.Single => PropertyAccessorType.Single,
|
||||||
|
TypeCode.Decimal => PropertyAccessorType.Decimal,
|
||||||
|
TypeCode.DateTime => PropertyAccessorType.DateTime,
|
||||||
|
TypeCode.Byte => PropertyAccessorType.Byte,
|
||||||
|
TypeCode.Int16 => PropertyAccessorType.Int16,
|
||||||
|
TypeCode.UInt16 => PropertyAccessorType.UInt16,
|
||||||
|
TypeCode.UInt32 => PropertyAccessorType.UInt32,
|
||||||
|
TypeCode.UInt64 => PropertyAccessorType.UInt64,
|
||||||
|
_ => PropertyAccessorType.Object
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeTypedGetter(Type declaringType, PropertyInfo prop)
|
||||||
|
{
|
||||||
|
switch (AccessorType)
|
||||||
|
{
|
||||||
|
case PropertyAccessorType.Int32:
|
||||||
|
Unsafe.AsRef(in _int32Getter) = AcSerializerCommon.CreateTypedGetter<int>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Int64:
|
||||||
|
Unsafe.AsRef(in _int64Getter) = AcSerializerCommon.CreateTypedGetter<long>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Boolean:
|
||||||
|
Unsafe.AsRef(in _boolGetter) = AcSerializerCommon.CreateTypedGetter<bool>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Double:
|
||||||
|
Unsafe.AsRef(in _doubleGetter) = AcSerializerCommon.CreateTypedGetter<double>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Single:
|
||||||
|
Unsafe.AsRef(in _floatGetter) = AcSerializerCommon.CreateTypedGetter<float>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Decimal:
|
||||||
|
Unsafe.AsRef(in _decimalGetter) = AcSerializerCommon.CreateTypedGetter<decimal>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.DateTime:
|
||||||
|
Unsafe.AsRef(in _dateTimeGetter) = AcSerializerCommon.CreateTypedGetter<DateTime>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Byte:
|
||||||
|
Unsafe.AsRef(in _byteGetter) = AcSerializerCommon.CreateTypedGetter<byte>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Int16:
|
||||||
|
Unsafe.AsRef(in _int16Getter) = AcSerializerCommon.CreateTypedGetter<short>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.UInt16:
|
||||||
|
Unsafe.AsRef(in _uint16Getter) = AcSerializerCommon.CreateTypedGetter<ushort>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.UInt32:
|
||||||
|
Unsafe.AsRef(in _uint32Getter) = AcSerializerCommon.CreateTypedGetter<uint>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.UInt64:
|
||||||
|
Unsafe.AsRef(in _uint64Getter) = AcSerializerCommon.CreateTypedGetter<ulong>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Guid:
|
||||||
|
Unsafe.AsRef(in _guidGetter) = AcSerializerCommon.CreateTypedGetter<Guid>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Enum:
|
||||||
|
Unsafe.AsRef(in _int32Getter) = AcSerializerCommon.CreateEnumGetter(declaringType, prop);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Typed Getters - Direct invocation, no cast!
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the property value from the target object.
|
/// Gets the property value from the target object (boxed).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public object? GetValue(object obj) => _getter(obj);
|
public object? GetDynamicValue(object obj) => _dynamicGetter(obj);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int GetInt32(object obj) => _int32Getter!(obj);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public long GetInt64(object obj) => _int64Getter!(obj);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool GetBoolean(object obj) => _boolGetter!(obj);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public double GetDouble(object obj) => _doubleGetter!(obj);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public float GetSingle(object obj) => _floatGetter!(obj);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public decimal GetDecimal(object obj) => _decimalGetter!(obj);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public DateTime GetDateTime(object obj) => _dateTimeGetter!(obj);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public byte GetByte(object obj) => _byteGetter!(obj);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public short GetInt16(object obj) => _int16Getter!(obj);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ushort GetUInt16(object obj) => _uint16Getter!(obj);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public uint GetUInt32(object obj) => _uint32Getter!(obj);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ulong GetUInt64(object obj) => _uint64Getter!(obj);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Guid GetGuid(object obj) => _guidGetter!(obj);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int GetEnumAsInt32(object obj) => _int32Getter!(obj);
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ namespace AyCode.Core.Serializers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base class for property accessors that also support setting values.
|
/// Base class for property accessors that also support setting values.
|
||||||
/// Used by deserializers. Extends PropertyAccessorBase with setter and IId collection support.
|
/// Used by deserializers. Extends PropertyAccessorBase with typed setters and IId collection support.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class PropertySetterBase : PropertyAccessorBase
|
public abstract class PropertySetterBase : PropertyAccessorBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compiled setter delegate for writing property values.
|
/// Compiled setter delegate for writing property values (boxed).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly Action<object, object?> _setter;
|
protected readonly Action<object, object?> _setter;
|
||||||
|
|
||||||
|
|
@ -36,11 +36,33 @@ public abstract class PropertySetterBase : PropertyAccessorBase
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<object, object?>? ElementIdGetter { get; }
|
public Func<object, object?>? ElementIdGetter { get; }
|
||||||
|
|
||||||
|
#region Strongly-typed setter delegate fields (eliminates runtime cast for deserialize)
|
||||||
|
|
||||||
|
// Only ONE of these is set based on AccessorType
|
||||||
|
private readonly Action<object, int>? _int32Setter;
|
||||||
|
private readonly Action<object, long>? _int64Setter;
|
||||||
|
private readonly Action<object, bool>? _boolSetter;
|
||||||
|
private readonly Action<object, double>? _doubleSetter;
|
||||||
|
private readonly Action<object, float>? _floatSetter;
|
||||||
|
private readonly Action<object, decimal>? _decimalSetter;
|
||||||
|
private readonly Action<object, DateTime>? _dateTimeSetter;
|
||||||
|
private readonly Action<object, byte>? _byteSetter;
|
||||||
|
private readonly Action<object, short>? _int16Setter;
|
||||||
|
private readonly Action<object, ushort>? _uint16Setter;
|
||||||
|
private readonly Action<object, uint>? _uint32Setter;
|
||||||
|
private readonly Action<object, ulong>? _uint64Setter;
|
||||||
|
private readonly Action<object, Guid>? _guidSetter;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
protected PropertySetterBase(PropertyInfo prop, Type declaringType)
|
protected PropertySetterBase(PropertyInfo prop, Type declaringType)
|
||||||
: base(prop, declaringType)
|
: base(prop, declaringType)
|
||||||
{
|
{
|
||||||
_setter = AcSerializerCommon.CreateCompiledSetter(declaringType, prop);
|
_setter = AcSerializerCommon.CreateCompiledSetter(declaringType, prop);
|
||||||
|
|
||||||
|
// Initialize typed setter
|
||||||
|
InitializeTypedSetter(declaringType, prop);
|
||||||
|
|
||||||
// Determine collection element type
|
// Determine collection element type
|
||||||
ElementType = GetCollectionElementType(PropertyType);
|
ElementType = GetCollectionElementType(PropertyType);
|
||||||
|
|
||||||
|
|
@ -63,9 +85,228 @@ public abstract class PropertySetterBase : PropertyAccessorBase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void InitializeTypedSetter(Type declaringType, PropertyInfo prop)
|
||||||
|
{
|
||||||
|
switch (AccessorType)
|
||||||
|
{
|
||||||
|
case PropertyAccessorType.Int32:
|
||||||
|
Unsafe.AsRef(in _int32Setter) = AcSerializerCommon.CreateTypedSetter<int>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Int64:
|
||||||
|
Unsafe.AsRef(in _int64Setter) = AcSerializerCommon.CreateTypedSetter<long>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Boolean:
|
||||||
|
Unsafe.AsRef(in _boolSetter) = AcSerializerCommon.CreateTypedSetter<bool>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Double:
|
||||||
|
Unsafe.AsRef(in _doubleSetter) = AcSerializerCommon.CreateTypedSetter<double>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Single:
|
||||||
|
Unsafe.AsRef(in _floatSetter) = AcSerializerCommon.CreateTypedSetter<float>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Decimal:
|
||||||
|
Unsafe.AsRef(in _decimalSetter) = AcSerializerCommon.CreateTypedSetter<decimal>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.DateTime:
|
||||||
|
Unsafe.AsRef(in _dateTimeSetter) = AcSerializerCommon.CreateTypedSetter<DateTime>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Byte:
|
||||||
|
Unsafe.AsRef(in _byteSetter) = AcSerializerCommon.CreateTypedSetter<byte>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Int16:
|
||||||
|
Unsafe.AsRef(in _int16Setter) = AcSerializerCommon.CreateTypedSetter<short>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.UInt16:
|
||||||
|
Unsafe.AsRef(in _uint16Setter) = AcSerializerCommon.CreateTypedSetter<ushort>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.UInt32:
|
||||||
|
Unsafe.AsRef(in _uint32Setter) = AcSerializerCommon.CreateTypedSetter<uint>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.UInt64:
|
||||||
|
Unsafe.AsRef(in _uint64Setter) = AcSerializerCommon.CreateTypedSetter<ulong>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Guid:
|
||||||
|
Unsafe.AsRef(in _guidSetter) = AcSerializerCommon.CreateTypedSetter<Guid>(declaringType, prop);
|
||||||
|
break;
|
||||||
|
case PropertyAccessorType.Enum:
|
||||||
|
Unsafe.AsRef(in _int32Setter) = AcSerializerCommon.CreateEnumSetter(declaringType, prop);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Typed Setters - Direct invocation, no cast!
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the property value on the target object.
|
/// Sets the property value on the target object (boxed).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public virtual void SetValue(object target, object? value) => _setter(target, value);
|
public virtual void SetValue(object target, object? value) => _setter(target, value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void SetInt32(object obj, int value) => _int32Setter!(obj, value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void SetInt64(object obj, long value) => _int64Setter!(obj, value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void SetBoolean(object obj, bool value) => _boolSetter!(obj, value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void SetDouble(object obj, double value) => _doubleSetter!(obj, value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void SetSingle(object obj, float value) => _floatSetter!(obj, value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void SetDecimal(object obj, decimal value) => _decimalSetter!(obj, value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void SetDateTime(object obj, DateTime value) => _dateTimeSetter!(obj, value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void SetByte(object obj, byte value) => _byteSetter!(obj, value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void SetInt16(object obj, short value) => _int16Setter!(obj, value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void SetUInt16(object obj, ushort value) => _uint16Setter!(obj, value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void SetUInt32(object obj, uint value) => _uint32Setter!(obj, value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void SetUInt64(object obj, ulong value) => _uint64Setter!(obj, value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void SetGuid(object obj, Guid value) => _guidSetter!(obj, value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void SetEnumAsInt32(object obj, int value) => _int32Setter!(obj, value);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Dispatch helpers - centralized switch for already-boxed values
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets property from already-boxed value using typed setter for unboxing optimization.
|
||||||
|
/// Use when value is already boxed (e.g., from ReadValue).
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void SetValueTyped(object target, object? value)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
SetValue(target, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (AccessorType)
|
||||||
|
{
|
||||||
|
case PropertyAccessorType.Int32:
|
||||||
|
SetInt32(target, (int)value);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Int64:
|
||||||
|
SetInt64(target, (long)value);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Boolean:
|
||||||
|
SetBoolean(target, (bool)value);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Double:
|
||||||
|
SetDouble(target, (double)value);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Single:
|
||||||
|
SetSingle(target, (float)value);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Decimal:
|
||||||
|
SetDecimal(target, (decimal)value);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.DateTime:
|
||||||
|
SetDateTime(target, (DateTime)value);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Byte:
|
||||||
|
SetByte(target, (byte)value);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Int16:
|
||||||
|
SetInt16(target, (short)value);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.UInt16:
|
||||||
|
SetUInt16(target, (ushort)value);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.UInt32:
|
||||||
|
SetUInt32(target, (uint)value);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.UInt64:
|
||||||
|
SetUInt64(target, (ulong)value);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Guid:
|
||||||
|
SetGuid(target, (Guid)value);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Enum:
|
||||||
|
SetEnumAsInt32(target, (int)value);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
SetValue(target, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets property to its type's default value using typed setter.
|
||||||
|
/// Avoids boxing for value types.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void SetToDefault(object target)
|
||||||
|
{
|
||||||
|
switch (AccessorType)
|
||||||
|
{
|
||||||
|
case PropertyAccessorType.Int32:
|
||||||
|
SetInt32(target, 0);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Int64:
|
||||||
|
SetInt64(target, 0L);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Boolean:
|
||||||
|
SetBoolean(target, false);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Double:
|
||||||
|
SetDouble(target, 0.0);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Single:
|
||||||
|
SetSingle(target, 0f);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Decimal:
|
||||||
|
SetDecimal(target, 0m);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.DateTime:
|
||||||
|
SetDateTime(target, default);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Byte:
|
||||||
|
SetByte(target, 0);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Int16:
|
||||||
|
SetInt16(target, 0);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.UInt16:
|
||||||
|
SetUInt16(target, 0);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.UInt32:
|
||||||
|
SetUInt32(target, 0);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.UInt64:
|
||||||
|
SetUInt64(target, 0);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Guid:
|
||||||
|
SetGuid(target, Guid.Empty);
|
||||||
|
return;
|
||||||
|
case PropertyAccessorType.Enum:
|
||||||
|
SetEnumAsInt32(target, 0);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
SetValue(target, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -247,7 +247,7 @@ public static partial class AcToonSerializer
|
||||||
// Write properties
|
// Write properties
|
||||||
foreach (var prop in metadata.Properties)
|
foreach (var prop in metadata.Properties)
|
||||||
{
|
{
|
||||||
var propValue = prop.GetValue(value);
|
var propValue = prop.GetDynamicValue(value);
|
||||||
|
|
||||||
// Skip null/default values if option is set
|
// Skip null/default values if option is set
|
||||||
if (context.Options.OmitDefaultValues && prop.IsDefaultValue(propValue))
|
if (context.Options.OmitDefaultValues && prop.IsDefaultValue(propValue))
|
||||||
|
|
|
||||||
|
|
@ -313,7 +313,7 @@ public static partial class AcToonSerializer
|
||||||
var metadata = GetTypeMetadata(type);
|
var metadata = GetTypeMetadata(type);
|
||||||
foreach (var prop in metadata.Properties)
|
foreach (var prop in metadata.Properties)
|
||||||
{
|
{
|
||||||
var propValue = prop.GetValue(value);
|
var propValue = prop.GetDynamicValue(value);
|
||||||
if (propValue != null) ScanReferences(propValue, context, depth + 1);
|
if (propValue != null) ScanReferences(propValue, context, depth + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue