From 35f6c3850edca52d5ae0f9a5961659f853ad8c27 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sun, 20 Apr 2025 23:33:00 +0300 Subject: [PATCH] fix: GetTypeData not including base class proprety & fields --- Engine.Serialization/Utils.cs | 65 +++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/Engine.Serialization/Utils.cs b/Engine.Serialization/Utils.cs index c021ed2..731dbb8 100644 --- a/Engine.Serialization/Utils.cs +++ b/Engine.Serialization/Utils.cs @@ -8,30 +8,67 @@ internal static class Utils internal static TypeData GetTypeData(this Type objectType) { - // IEnumerable eventInfos = objectType.GetEvents(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) - // .OrderBy(ei => ei.Name) - // .ToList(); - List fieldInfos = objectType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) - .OrderBy(ei => ei.Name) //ei => ei.FieldType.IsPrimitive || ei.FieldType == typeof(string)) - // .ThenByDescending(ei => ei.Name) - .ToList(); + List fieldInfos = GetFieldInfosIncludingBaseClasses(objectType, BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public); - List propertyInfos = objectType.GetProperties(BindingFlags.Instance | BindingFlags.Public) + List propertyInfos = GetPropertyInfosIncludingBaseClasses(objectType, BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.Public) .Where(pi => pi.SetMethod is not null) - .OrderBy(ei => ei.Name)// ei => ei.PropertyType.IsPrimitive || ei.PropertyType == typeof(string)) - // .ThenByDescending(ei => ei.Name) .ToList(); propertyInfos.AddRange( - objectType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic) + GetPropertyInfosIncludingBaseClasses(objectType, BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic) .Where(pi => pi.SetMethod is not null && pi.GetCustomAttribute() is not null) - .OrderBy(ei => ei.Name)// ei => ei.PropertyType.IsPrimitive || ei.PropertyType == typeof(string)) - // .ThenByDescending(ei => ei.Name) - .ToList() ); return new TypeData(fieldInfos, propertyInfos); } + + internal static List GetFieldInfosIncludingBaseClasses(Type type, BindingFlags bindingFlags) + { + if (type.BaseType is null) + return [.. type.GetFields(bindingFlags)]; + + Type currentType = type; + FieldInfoComparer fieldComparer = new(); + HashSet fieldInfoList = new(type.GetFields(bindingFlags), fieldComparer); + + while (currentType.BaseType is Type baseType) + { + currentType = baseType; + fieldInfoList.UnionWith(currentType!.GetFields(bindingFlags)); + } + + return [.. fieldInfoList.OrderBy(fi => fi.Name)]; + } + + internal static List GetPropertyInfosIncludingBaseClasses(Type type, BindingFlags bindingFlags) + { + if (type.BaseType is null) + return [.. type.GetProperties(bindingFlags)]; + + Type currentType = type; + PropertyInfoComparer propertyComparer = new(); + HashSet propertyInfoList = new(type.GetProperties(bindingFlags), propertyComparer); + + while (currentType.BaseType is Type baseType) + { + currentType = baseType; + propertyInfoList.UnionWith(currentType.GetProperties(bindingFlags)); + } + + return [.. propertyInfoList.OrderBy(pi => pi.Name)]; + } + + private class FieldInfoComparer : IEqualityComparer + { + public bool Equals(FieldInfo? x, FieldInfo? y) => x?.DeclaringType == y?.DeclaringType && x?.Name == y?.Name; + public int GetHashCode(FieldInfo obj) => obj.Name.GetHashCode() ^ obj.DeclaringType!.GetHashCode(); + } + + private class PropertyInfoComparer : IEqualityComparer + { + public bool Equals(PropertyInfo? x, PropertyInfo? y) => x?.DeclaringType == y?.DeclaringType && x?.Name == y?.Name; + public int GetHashCode(PropertyInfo obj) => obj.Name.GetHashCode() ^ obj.DeclaringType!.GetHashCode(); + } } internal record struct TypeData(IEnumerable Fields, IEnumerable Properties)