using LiveForum.Code.AttributeExtend; using System.Collections; using System.Collections.Concurrent; using System.Linq.Expressions; using System.Reflection; namespace LiveForum.Code.Utility; /// /// object转数据字典 /// public static class ObjectExtensions { /// /// 用于存储每种类型的属性访问器数组的线程安全缓存。 /// private static readonly ConcurrentDictionary PropertyCache = new(); /// /// 缓存每个属性是否具有 ImagesAttribute 特性。 /// public static readonly ConcurrentDictionary _PropertyCache = new(); /// /// 判断对象是否为原始类型或字符串类型。 /// /// 要检查的对象。 /// 如果对象是原始类型或字符串,返回 true;否则返回 false。 public static bool IsPrimitiveType(object obj) => obj is not null && (obj.GetType().IsPrimitive || obj is string or ValueType); /// /// 判断对象是否为集合类型(不包括字符串)。 /// /// 要检查的对象。 /// 如果对象是集合类型(但不是字符串),返回 true;否则返回 false。 public static bool IsCollectionType(object obj) => obj is IEnumerable && obj is not string; /// /// 根据对象类型,将对象转换为字典或列表格式,支持可选的路径前缀。 /// /// 要转换的对象。 /// 属性路径的可选前缀。 /// 对象的字典或列表表示形式。 public static object ToDictionaryOrList(this object obj, bool isEnumerable, string prefix = "", Func? imageFunc = null, Func? languageFunc = null) { if (obj == null) return null; return obj switch { _ when IsPrimitiveType(obj) => obj, IEnumerable enumerable => TransformCollection(enumerable, prefix, imageFunc, languageFunc), _ => TransformObject(obj, isEnumerable, prefix, imageFunc, languageFunc) }; } /// /// 将集合对象转换为包含转换项的列表,每个项保留其路径前缀。 /// /// 要转换的集合。 /// 集合中每个属性路径的前缀。 /// 转换后的项列表。 private static List TransformCollection(IEnumerable enumerable, string prefix = "", Func? imageFunc = null, Func? languageFunc = null) { var list = new List(enumerable is ICollection collection ? collection.Count : 10); int index = 0; foreach (var item in enumerable) { list.Add(ToDictionaryOrList(item, true, $"{prefix}.[{index}]", imageFunc, languageFunc)); // 为集合中每个项添加路径 index++; } return list; } /// /// 将对象的属性转换为带有路径前缀的字典,并应用前缀规则。 /// /// 要转换的对象。 /// 每个属性路径的前缀。 /// 包含属性名和属性值的字典。 private static Dictionary TransformObject(object obj, bool isEnumerable, string prefix = "", Func? imageFunc = null, Func? languageFunc = null) { if (obj == null) { return null; } var type = obj.GetType(); var accessors = PropertyCache.GetOrAdd(type, CreatePropertyAccessors); var keyValuePairs = new Dictionary(accessors.Length); foreach (var accessor in accessors) { var propertyPath = $"{prefix}.{accessor.PropertyName}"; // 构建完整的属性路径 // 使用访问器获取属性值 var propertyValue = accessor.Getter(obj); // 如果属性是字符串,在其值前添加 "test" if (propertyValue is string stringValue) { if (stringValue == null) { keyValuePairs[accessor.PropertyName] = ""; continue; } if (!string.IsNullOrEmpty(stringValue) && languageFunc != null) { stringValue = languageFunc?.Invoke(stringValue, propertyPath, accessor.PropertyName, isEnumerable) ?? ""; } keyValuePairs[accessor.PropertyName] = stringValue; continue; } // 如果属性具有 ImagesAttribute,在其值前添加 "image" // 否则,如果是集合类型,则递归转换 keyValuePairs[accessor.PropertyName] = accessor.HasImagesAttribute ? imageFunc?.Invoke((int)propertyValue) ?? "" : ToDictionaryOrList(propertyValue, isEnumerable, propertyPath, imageFunc, languageFunc); // IsCollectionType(propertyValue) ?: propertyValue; } return keyValuePairs; } /// /// 为给定类型创建属性访问器数组。 /// /// 要创建属性访问器的类型。 /// 属性访问器数组。 private static PropertyAccessor[] CreatePropertyAccessors(Type type) { var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); return properties.Select(property => { // 创建用于访问属性值的委托 var getter = CreatePropertyGetter(type, property); // 检查属性是否具有 ImagesAttribute,并将结果存储在缓存中 var isImagesAttribute = _PropertyCache.GetOrAdd(property, p => p.GetCustomAttribute() != null); return new PropertyAccessor(property.Name, getter, isImagesAttribute); }).ToArray(); } private static Func CreatePropertyGetter(Type type, PropertyInfo property) { var parameter = Expression.Parameter(typeof(object), "obj"); var castParameter = Expression.Convert(parameter, type); var propertyAccess = Expression.Property(castParameter, property); var convertPropertyAccess = Expression.Convert(propertyAccess, typeof(object)); return Expression.Lambda>(convertPropertyAccess, parameter).Compile(); } private class PropertyAccessor { public string PropertyName { get; } public Func Getter { get; } public bool HasImagesAttribute { get; } public PropertyAccessor(string propertyName, Func getter, bool hasImagesAttribute) { PropertyName = propertyName; Getter = getter; HasImagesAttribute = hasImagesAttribute; } } }