diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CustomResultFilter.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CustomResultFilter.cs index 7b76eba..58e4cee 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CustomResultFilter.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CustomResultFilter.cs @@ -16,6 +16,9 @@ using System.Reflection; namespace CloudGaming.Code.AppExtend { + /// + /// + /// public class CustomResultFilter : IResultFilter { private readonly IHttpContextAccessor _httpContextAccessor; @@ -37,22 +40,20 @@ namespace CloudGaming.Code.AppExtend //Dictionary keyValuePairs = new Dictionary(); if (context.Result is ObjectResult objectResult && objectResult.Value != null) { - //if (IsPrimitiveOrString(objectResult.Value.GetType())) - //{ - - //} - - var dic = objectResult.Value.ToDictionaryOrList(); - var t = objectResult.Value.GetType(); - if (!t.FullName.Contains("HuanMeng.DotNetCore.Base.BaseResponse")) + var x = objectResult.Value.GetType(); + object? value = null; + if (!x.FullName.Contains("HuanMeng.DotNetCore.Base.BaseResponse")) { - BaseResponse baseResponse = new BaseResponse(ResonseCode.Success, "", dic); - objectResult.Value = baseResponse; + BaseResponse baseResponse = new BaseResponse(ResonseCode.Success, "", objectResult.Value); + value = baseResponse; } else { - objectResult.Value = dic; + value = objectResult.Value; } + + var dic = value.ToDictionaryOrList(); + objectResult.Value = dic; //if (objectResult.Value is IEnumerable enumerable) //{ // var list = objectResult.Value.ToListDictionary(5); diff --git a/src/CloudGaming/Code/CloudGaming.Code/CloudGaming.Code.csproj b/src/CloudGaming/Code/CloudGaming.Code/CloudGaming.Code.csproj index 24f5cb2..adf1556 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/CloudGaming.Code.csproj +++ b/src/CloudGaming/Code/CloudGaming.Code/CloudGaming.Code.csproj @@ -11,6 +11,7 @@ + diff --git a/src/CloudGaming/Console/CloudGaming.Test/ObjectExtensions.cs b/src/CloudGaming/Console/CloudGaming.Test/ObjectExtensions.cs deleted file mode 100644 index 3beac1d..0000000 --- a/src/CloudGaming/Console/CloudGaming.Test/ObjectExtensions.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -namespace CloudGaming.Test; - - - -public static class ObjectExtensions -{ - private static readonly ConcurrentDictionary PropertyCache = new(); - private static readonly ConcurrentDictionary FieldCache = new(); - - public static Dictionary ToDictionary(this object obj, int maxDepth = 3) - { - return ToDictionaryRecursive(obj, maxDepth); - } - - private static Dictionary ToDictionaryRecursive(object obj, int depth) - { - if (obj == null || depth < 0) - return null; - - var dictionary = new Dictionary(); - var type = obj.GetType(); - - var properties = PropertyCache.GetOrAdd(type, t => t.GetProperties(BindingFlags.Public | BindingFlags.Instance)); - foreach (var property in properties) - { - var value = property.GetValue(obj); - dictionary[property.Name] = ConvertValue(value, depth - 1); - } - - var fields = FieldCache.GetOrAdd(type, t => t.GetFields(BindingFlags.Public | BindingFlags.Instance)); - foreach (var field in fields) - { - var value = field.GetValue(obj); - dictionary[field.Name] = ConvertValue(value, depth - 1); - } - - return dictionary; - } - - private static object ConvertValue(object value, int depth) - { - if (value == null) return null; - - if (value is IEnumerable enumerable && !(value is string)) - { - var list = new List(); - foreach (var item in enumerable) - { - list.Add(ConvertValue(item, depth - 1)); - } - return list; - } - - if (value.GetType().IsClass && !(value is string)) - { - return ToDictionaryRecursive(value, depth - 1); - } - - return value; - } -} diff --git a/src/CloudGaming/Console/CloudGaming.Test/Program.cs b/src/CloudGaming/Console/CloudGaming.Test/Program.cs index 1b850f9..4d09ea7 100644 --- a/src/CloudGaming/Console/CloudGaming.Test/Program.cs +++ b/src/CloudGaming/Console/CloudGaming.Test/Program.cs @@ -1,24 +1,66 @@ // See https://aka.ms/new-console-template for more information using CloudGaming.DtoModel; -using CloudGaming.Test; + + +using HuanMeng.DotNetCore.Utility; using Newtonsoft.Json; using System.Reflection; -Console.WriteLine("Hello, World!"); -AppConfigDto appConfigDto = new AppConfigDto() +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Running; +using CloudGaming.DtoModel.Epg; + +namespace CloudGaming.Test { - //AppConfigExtend = new AppConfigExtend() - //{ - // TestImage = 1 - //}, - //CheckingGames = new List() { "iu","cg111"}, - //IsChecking = true, - //HighDelay = true, - //OpenImage = 1, - //MyProperty = "test" -}; -var dic = appConfigDto.ToDictionary(15); -Console.WriteLine(JsonConvert.SerializeObject(dic)); -Console.ReadKey(); + [MemoryDiagnoser] + [MarkdownExporterAttribute.GitHub] + [SimpleJob(RuntimeMoniker.Net80, baseline: true)] + [RPlotExporter] + public class Program + { + + static void Main(string[] args) + { + //List epgCategoryDtos = new List(); + //for (int i = 0; i < 20; i++) + //{ + // epgCategoryDtos.Add(new EpgCategoryDto() + // { + // CategoryName = "Banner" + i, + // CategoryType = "Banner" + i, + // IsQuickStartPopUp = true, + // ShowNum_Index = 10 + i, + // EpgList = new List() + // { + // new EpgInfo(){ + // CornerIcon=i, + // EpgId=i, + // IdName="IO"+i, + // ResType=4, + // Pic=1, + // Title="title"+i + // }, + // new EpgInfo(){ + // CornerIcon=i+2, + // EpgId=i+2, + // IdName="IO"+i+2, + // ResType=4+2, + // Pic=2, + // Title="title"+i+2 + // } + // }, + // }); + //} + + + //var dic = ObjectExtensions5.ToDictionaryOrList(epgCategoryDtos); + //Console.WriteLine(JsonConvert.SerializeObject(dic)); + BenchmarkRunner.Run(); + } + } +} + + diff --git a/src/CloudGaming/Console/CloudGaming.Test/ToDictionaryOrListTest.cs b/src/CloudGaming/Console/CloudGaming.Test/ToDictionaryOrListTest.cs new file mode 100644 index 0000000..d8fdd4d --- /dev/null +++ b/src/CloudGaming/Console/CloudGaming.Test/ToDictionaryOrListTest.cs @@ -0,0 +1,478 @@ +using BenchmarkDotNet.Attributes; + +using CloudGaming.DtoModel; +using CloudGaming.DtoModel.Epg; + +using HuanMeng.DotNetCore.Utility; + +using Newtonsoft.Json; + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CloudGaming.Test +{ + [MemoryDiagnoser] + public class ToDictionaryOrListTest + { + public ToDictionaryOrListTest() + { + + } + + + //[Benchmark] + public object ObjectToDictionaryOrListTest1() + { + EpgCategoryDto epgCategoryDto = new EpgCategoryDto() + { + CategoryName = "Banner", + CategoryType = "Banner", + IsQuickStartPopUp = true, + ShowNum_Index = 10, + EpgList = new List() + { + new EpgInfo(){ + CornerIcon=10, + EpgId=10, + IdName="IO", + ResType=4, + Title="title" + } + }, + }; + + var dic = epgCategoryDto.ToDictionaryOrList(); + return dic; + + } + + + //[Benchmark] + public object ListToDictionaryOrListTest1() + { + + List epgCategoryDtos = new List(); + for (int i = 0; i < 20; i++) + { + epgCategoryDtos.Add(new EpgCategoryDto() + { + CategoryName = "Banner" + i, + CategoryType = "Banner" + i, + IsQuickStartPopUp = true, + ShowNum_Index = 10 + i, + EpgList = new List() + { + new EpgInfo(){ + CornerIcon=i, + EpgId=i, + IdName="IO"+i, + ResType=4, + Title="title"+i + }, + new EpgInfo(){ + CornerIcon=i+2, + EpgId=i+2, + IdName="IO"+i+2, + ResType=4+2, + Title="title"+i+2 + } + }, + }); + } + + var dic = epgCategoryDtos.ToDictionaryOrList(); + return dic; + + } + + + //[Benchmark] + public object ObjectToDictionaryOrListExtend1() + { + EpgCategoryDto epgCategoryDto = new EpgCategoryDto() + { + CategoryName = "Banner", + CategoryType = "Banner", + IsQuickStartPopUp = true, + ShowNum_Index = 10, + EpgList = new List() + { + new EpgInfo(){ + CornerIcon=10, + EpgId=10, + IdName="IO", + ResType=4, + Title="title" + } + }, + }; + var dic = ObjectExtensions1.ToDictionaryOrList(epgCategoryDto); + return dic; + + } + + + //[Benchmark] + public object ListToDictionaryOrListExtend1() + { + List epgCategoryDtos = new List(); + for (int i = 0; i < 20; i++) + { + epgCategoryDtos.Add(new EpgCategoryDto() + { + CategoryName = "Banner" + i, + CategoryType = "Banner" + i, + IsQuickStartPopUp = true, + ShowNum_Index = 10 + i, + EpgList = new List() + { + new EpgInfo(){ + CornerIcon=i, + EpgId=i, + IdName="IO"+i, + ResType=4, + Title="title"+i + }, + new EpgInfo(){ + CornerIcon=i+2, + EpgId=i+2, + IdName="IO"+i+2, + ResType=4+2, + Title="title"+i+2 + } + }, + }); + } + + + var dic = ObjectExtensions1.ToDictionaryOrList(epgCategoryDtos); + return dic; + + } + + + + //[Benchmark] + public object ObjectToDictionaryOrListExtend3() + { + EpgCategoryDto epgCategoryDto = new EpgCategoryDto() + { + CategoryName = "Banner", + CategoryType = "Banner", + IsQuickStartPopUp = true, + ShowNum_Index = 10, + EpgList = new List() + { + new EpgInfo(){ + CornerIcon=10, + EpgId=10, + IdName="IO", + ResType=4, + Title="title" + } + }, + }; + var dic = ObjectExtensions3.ToDictionaryOrList(epgCategoryDto); + return dic; + + } + + + //[Benchmark] + public object ListToDictionaryOrListExtend3() + { + List epgCategoryDtos = new List(); + for (int i = 0; i < 20; i++) + { + epgCategoryDtos.Add(new EpgCategoryDto() + { + CategoryName = "Banner" + i, + CategoryType = "Banner" + i, + IsQuickStartPopUp = true, + ShowNum_Index = 10 + i, + EpgList = new List() + { + new EpgInfo(){ + CornerIcon=i, + EpgId=i, + IdName="IO"+i, + ResType=4, + Title="title"+i + }, + new EpgInfo(){ + CornerIcon=i+2, + EpgId=i+2, + IdName="IO"+i+2, + ResType=4+2, + Title="title"+i+2 + } + }, + }); + } + + + var dic = ObjectExtensions3.ToDictionaryOrList(epgCategoryDtos); + return dic; + + } + + [Benchmark] + public object ObjectToDictionaryOrListExtend4() + { + EpgCategoryDto epgCategoryDto = new EpgCategoryDto() + { + CategoryName = "Banner", + CategoryType = "Banner", + IsQuickStartPopUp = true, + ShowNum_Index = 10, + EpgList = new List() + { + new EpgInfo(){ + CornerIcon=10, + EpgId=10, + IdName="IO", + ResType=4, + Title="title" + } + }, + }; + var dic = ObjectExtensions5.ToDictionaryOrList(epgCategoryDto); + return dic; + + } + + + [Benchmark] + public object ListToDictionaryOrListExtend4() + { + List epgCategoryDtos = new List(); + for (int i = 0; i < 20; i++) + { + epgCategoryDtos.Add(new EpgCategoryDto() + { + CategoryName = "Banner" + i, + CategoryType = "Banner" + i, + IsQuickStartPopUp = true, + ShowNum_Index = 10 + i, + EpgList = new List() + { + new EpgInfo(){ + CornerIcon=i, + EpgId=i, + IdName="IO"+i, + ResType=4, + Title="title"+i + }, + new EpgInfo(){ + CornerIcon=i+2, + EpgId=i+2, + IdName="IO"+i+2, + ResType=4+2, + Title="title"+i+2 + } + }, + }); + } + + + var dic = ObjectExtensions5.ToDictionaryOrList(epgCategoryDtos); + return dic; + + } + + [Benchmark] + public object ObjectToDictionaryOrListExtend6() + { + EpgCategoryDto epgCategoryDto = new EpgCategoryDto() + { + CategoryName = "Banner", + CategoryType = "Banner", + IsQuickStartPopUp = true, + ShowNum_Index = 10, + EpgList = new List() + { + new EpgInfo(){ + CornerIcon=10, + EpgId=10, + IdName="IO", + ResType=4, + Title="title" + } + }, + }; + var dic = ObjectExtensions6.ToDictionaryOrList(epgCategoryDto); + return dic; + + } + + + [Benchmark] + public object ListToDictionaryOrListExtend6() + { + List epgCategoryDtos = new List(); + for (int i = 0; i < 20; i++) + { + epgCategoryDtos.Add(new EpgCategoryDto() + { + CategoryName = "Banner" + i, + CategoryType = "Banner" + i, + IsQuickStartPopUp = true, + ShowNum_Index = 10 + i, + EpgList = new List() + { + new EpgInfo(){ + CornerIcon=i, + EpgId=i, + IdName="IO"+i, + ResType=4, + Title="title"+i + }, + new EpgInfo(){ + CornerIcon=i+2, + EpgId=i+2, + IdName="IO"+i+2, + ResType=4+2, + Title="title"+i+2 + } + }, + }); + } + + + var dic = ObjectExtensions6.ToDictionaryOrList(epgCategoryDtos); + return dic; + } + + + [Benchmark] + public object ObjectToDictionaryOrListExtend7() + { + EpgCategoryDto epgCategoryDto = new EpgCategoryDto() + { + CategoryName = "Banner", + CategoryType = "Banner", + IsQuickStartPopUp = true, + ShowNum_Index = 10, + EpgList = new List() + { + new EpgInfo(){ + CornerIcon=10, + EpgId=10, + IdName="IO", + ResType=4, + Title="title" + } + }, + }; + var dic = ObjectExtensions6.ToDictionaryOrList(epgCategoryDto); + return dic; + + } + + + [Benchmark] + public object ListToDictionaryOrListExtend7() + { + List epgCategoryDtos = new List(); + for (int i = 0; i < 20; i++) + { + epgCategoryDtos.Add(new EpgCategoryDto() + { + CategoryName = "Banner" + i, + CategoryType = "Banner" + i, + IsQuickStartPopUp = true, + ShowNum_Index = 10 + i, + EpgList = new List() + { + new EpgInfo(){ + CornerIcon=i, + EpgId=i, + IdName="IO"+i, + ResType=4, + Title="title"+i + }, + new EpgInfo(){ + CornerIcon=i+2, + EpgId=i+2, + IdName="IO"+i+2, + ResType=4+2, + Title="title"+i+2 + } + }, + }); + } + + + var dic = ObjectExtensions7.ToDictionaryOrList(epgCategoryDtos); + return dic; + } + + [Benchmark] + public object ObjectToDictionaryOrListExtend8() + { + EpgCategoryDto epgCategoryDto = new EpgCategoryDto() + { + CategoryName = "Banner", + CategoryType = "Banner", + IsQuickStartPopUp = true, + ShowNum_Index = 10, + EpgList = new List() + { + new EpgInfo(){ + CornerIcon=10, + EpgId=10, + IdName="IO", + ResType=4, + Title="title" + } + }, + }; + var dic = ObjectExtensions.ToDictionaryOrList(epgCategoryDto); + return dic; + + } + + + [Benchmark] + public object ListToDictionaryOrListExtend8() + { + List epgCategoryDtos = new List(); + for (int i = 0; i < 20; i++) + { + epgCategoryDtos.Add(new EpgCategoryDto() + { + CategoryName = "Banner" + i, + CategoryType = "Banner" + i, + IsQuickStartPopUp = true, + ShowNum_Index = 10 + i, + EpgList = new List() + { + new EpgInfo(){ + CornerIcon=i, + EpgId=i, + IdName="IO"+i, + ResType=4, + Title="title"+i + }, + new EpgInfo(){ + CornerIcon=i+2, + EpgId=i+2, + IdName="IO"+i+2, + ResType=4+2, + Title="title"+i+2 + } + }, + }); + } + + + var dic = ObjectExtensions.ToDictionaryOrList(epgCategoryDtos); + return dic; + } + } +} diff --git a/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_Phone/T_Epg_CategoryCfg.cs b/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_Phone/T_Epg_CategoryCfg.cs index 8aef8b6..30511ee 100644 --- a/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_Phone/T_Epg_CategoryCfg.cs +++ b/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_Phone/T_Epg_CategoryCfg.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace CloudGaming.Model.DbSqlServer.Db_Phone; diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/ObjectExtensions.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/ObjectExtensions.cs deleted file mode 100644 index b8ce46d..0000000 --- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/ObjectExtensions.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using HuanMeng.DotNetCore.AttributeExtend; -using Newtonsoft.Json.Linq; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace HuanMeng.DotNetCore.Utility; - -public static class ObjectExtensions -{ - /// - /// 缓存属性信息以提高反射访问的性能,使用 ConcurrentDictionary 确保线程安全 - /// - private static readonly ConcurrentDictionary PropertyCache = new(); - /// - /// 缓存字段信息以提高反射访问的性能,使用 ConcurrentDictionary 确保线程安全 - /// - private static readonly ConcurrentDictionary FieldCache = new(); - - - - /// - /// 判断对象是否为基础类型。 - /// - public static bool IsPrimitiveType(object obj) - { - if (obj == null) return false; - - Type type = obj.GetType(); - return type.IsPrimitive || type.IsValueType || type == typeof(string); - } - - /// - /// 判断对象是否为集合类型(排除字符串)。 - /// - public static bool IsCollectionType(object obj) - { - if (obj == null) return false; - - return obj is IEnumerable && !(obj is string); - } - - /// - /// 判断对象是否为复杂类型。 - /// - public static bool IsComplexType(object obj) - { - if (obj == null) return false; - - Type type = obj.GetType(); - // 非基础类型且非集合类型的对象即为复杂类型 - return !IsPrimitiveType(obj) && !IsCollectionType(obj); - } - - public static object ToDictionaryOrList(this object obj) - { - if (obj == null) - { - return null; - } - Type type = obj.GetType(); - //判断对象是否是基础类型的 - if (type.IsPrimitive || type.IsValueType || type == typeof(string)) - { - return type; - } - //判断对象是否是数组类型的 - else if (obj is IEnumerable enumerable && !(obj is string)) - { - List list = new List(); - foreach (var item in enumerable) - { - list.Add(ToDictionaryOrList(item)); - } - return list; - } - else - { - //object类型 - Dictionary keyValuePairs = new Dictionary(); - var properties = PropertyCache.GetOrAdd(type, t => t.GetProperties(BindingFlags.Public | BindingFlags.Instance)); - foreach (var property in properties) - { - - var imagesAttribute = property.GetCustomAttribute(); - var objstr = property.GetValue(obj); - //对象中判断字段是否是数组 - if (objstr is IEnumerable enumerable1 && !(objstr is string)) - { - keyValuePairs[property.Name] = ToDictionaryOrList(objstr); - continue; - } - keyValuePairs[property.Name] = objstr; - } - return keyValuePairs; - } - } - -} \ No newline at end of file diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/ObjectExtensions1.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/ObjectExtensions1.cs new file mode 100644 index 0000000..1e907d4 --- /dev/null +++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/ObjectExtensions1.cs @@ -0,0 +1,726 @@ +using System; +using System.Collections.Concurrent; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using HuanMeng.DotNetCore.AttributeExtend; +using System.Linq.Expressions; + + +namespace HuanMeng.DotNetCore.Utility; + + +/// +/// +/// +public static class ObjectExtensions +{ + /// + /// 线程安全的缓存,用于存储每个类型的属性访问器数组 + /// + private static readonly ConcurrentDictionary PropertyCache = new(); + /// + /// 缓存每个属性是否具有 ImagesAttribute 特性 + /// + public static readonly ConcurrentDictionary _PropertyCache = new(); + + /// + /// 判断对象是否为原始类型或字符串类型 + /// + /// + /// + public static bool IsPrimitiveType(object obj) => + obj is not null && (obj.GetType().IsPrimitive || obj is string or ValueType); + + /// + /// 判断对象是否为集合类型(但排除字符串) + /// + /// + /// + public static bool IsCollectionType(object obj) => obj is IEnumerable && obj is not string; + + /// + /// 将对象转换为字典或列表 + /// + /// + /// + public static object ToDictionaryOrList(this object obj) + { + if (obj == null) return null; + + return obj switch + { + // 如果是原始类型,直接返回对象 + _ when IsPrimitiveType(obj) => obj, + // 如果是集合类型,转换为列表 + IEnumerable enumerable => TransformCollection(enumerable), + // 否则,将对象转换为字典 + _ => TransformObject(obj) + }; + } + + /// + /// 将集合类型转换为列表 + /// + /// + /// + private static List TransformCollection(IEnumerable enumerable) + { + // 如果集合实现了 ICollection 接口,使用其 Count 属性初始化列表容量 + var list = new List(enumerable is ICollection collection ? collection.Count : 10); + foreach (var item in enumerable) + { + // 递归调用 ToDictionaryOrList 对集合中的每个项进行转换 + list.Add(ToDictionaryOrList(item)); + } + return list; + } + + /// + /// 将对象的属性转换为字典 + /// + /// + /// + private static Dictionary TransformObject(object obj, string prefix = "") + { + var type = obj.GetType(); + // 获取或创建该类型的属性访问器数组 + var accessors = PropertyCache.GetOrAdd(type, CreatePropertyAccessors); + var keyValuePairs = new Dictionary(accessors.Length); + + foreach (var accessor in accessors) + { + // 通过属性访问器获取属性值 + var propertyValue = accessor.Getter(obj); + + // 如果属性值是字符串类型,添加 "test" 前缀 + if (propertyValue is string stringValue) + { + //propertyValue =; + keyValuePairs[accessor.PropertyName] = $"test{stringValue}"; + continue; + } + + // 如果属性有 ImagesAttribute 特性,值前面加上 "image" 前缀 + // 否则,如果属性是集合类型,递归转换 + keyValuePairs[accessor.PropertyName] = accessor.HasImagesAttribute + ? $"image{propertyValue}" + : IsCollectionType(propertyValue) ? ToDictionaryOrList(propertyValue) : propertyValue; + } + + return keyValuePairs; + } + + // 为给定类型创建属性访问器数组 + private static PropertyAccessor[] CreatePropertyAccessors(Type type) + { + var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); + return properties.Select(property => + { + // 创建属性的 Getter 委托 + var getter = CreatePropertyGetter(type, property); + // 使用缓存检查属性是否具有 ImagesAttribute 特性 + bool hasImagesAttribute = _PropertyCache.GetOrAdd(property, p => p.GetCustomAttribute() != null); + return new PropertyAccessor(property.Name, getter, hasImagesAttribute); + }).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)); // 转换属性值为 object + return Expression.Lambda>(convertPropertyAccess, parameter).Compile(); // 编译为委托 + } + + // 属性访问器类,包含属性名、Getter 和特性标志 + 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; + } + } +} + + +[Obsolete] +public static class ObjectExtensions11 +{ + /// + /// 缓存属性信息以提高反射访问的性能,使用 ConcurrentDictionary 确保线程安全 + /// + private static readonly ConcurrentDictionary PropertyCache = new(); + + /// + /// 判断对象是否为基础类型。 + /// + public static bool IsPrimitiveType(object obj) => + obj is not null && (obj.GetType().IsPrimitive || obj is string or ValueType); + + /// + /// 判断对象是否为集合类型(排除字符串)。 + /// + public static bool IsCollectionType(object obj) => obj is IEnumerable && obj is not string; + + /// + /// 判断对象是否为复杂类型。 + /// + public static bool IsComplexType(object obj) => + obj is not null && !IsPrimitiveType(obj) && !IsCollectionType(obj); + + /// + /// 转换对象为字典或列表 + /// + public static object ToDictionaryOrList(object obj) + { + if (obj == null) return null; + + return obj switch + { + // 基础类型 + _ when IsPrimitiveType(obj) => obj, + // 集合类型 + IEnumerable enumerable => TransformCollection(enumerable), + // 复杂类型 + _ => TransformObject(obj) + }; + } + + /// + /// 转换集合为列表 + /// + private static List TransformCollection(IEnumerable enumerable) + { + var list = new List(); + foreach (var item in enumerable) + { + list.Add(item.ToDictionaryOrList()); + } + return list; + } + + /// + /// 转换对象为字典 + /// + private static Dictionary TransformObject(object obj) + { + var type = obj.GetType(); + var properties = PropertyCache.GetOrAdd(type, t => t.GetProperties(BindingFlags.Public | BindingFlags.Instance)); + var keyValuePairs = new Dictionary(); + + foreach (var property in properties) + { + var propertyValue = property.GetValue(obj); + keyValuePairs[property.Name] = IsCollectionType(propertyValue) + ? propertyValue.ToDictionaryOrList() + : propertyValue; + } + + return keyValuePairs; + } +} + +[Obsolete] +public static class ObjectExtensions1 +{ + private static readonly ConcurrentDictionary PropertyCache = new(); + private static readonly ConcurrentDictionary HasImagesAttributeCache = new(); + + public static bool IsPrimitiveType(object obj) => + obj is not null && (obj.GetType().IsPrimitive || obj is string or ValueType); + + public static bool IsCollectionType(object obj) => obj is IEnumerable && obj is not string; + + public static bool IsComplexType(object obj) => + obj is not null && !IsPrimitiveType(obj) && !IsCollectionType(obj); + + public static object ToDictionaryOrList(object obj) + { + if (obj == null) return null; + + return obj switch + { + _ when IsPrimitiveType(obj) => obj, + IEnumerable enumerable => TransformCollection(enumerable), + _ => TransformObject(obj) + }; + } + + private static List TransformCollection(IEnumerable enumerable) + { + var list = new List(enumerable is ICollection collection ? collection.Count : 10); + foreach (var item in enumerable) + { + list.Add(ToDictionaryOrList(item)); + } + return list; + } + + private static Dictionary TransformObject(object obj) + { + var type = obj.GetType(); + var properties = PropertyCache.GetOrAdd(type, t => t.GetProperties(BindingFlags.Public | BindingFlags.Instance)); + var keyValuePairs = new Dictionary(properties.Length); + + foreach (var property in properties) + { + // 预先缓存 ImagesAttribute 判断结果 + if (!HasImagesAttributeCache.TryGetValue(property, out bool hasImagesAttribute)) + { + hasImagesAttribute = property.GetCustomAttribute() != null; + HasImagesAttributeCache.TryAdd(property, hasImagesAttribute); + } + var propertyValue = property.GetValue(obj); + if (hasImagesAttribute) + { + keyValuePairs[property.Name] = $"image{propertyValue}"; + continue; + } + + keyValuePairs[property.Name] = IsCollectionType(propertyValue) + ? ToDictionaryOrList(propertyValue) + : propertyValue; + if (propertyValue is IEnumerable imagesEnumerable) + { + keyValuePairs[property.Name] = TransformCollection(imagesEnumerable); + } + } + + return keyValuePairs; + } +} +[Obsolete] +public static class ObjectExtensions3 +{ + private static readonly ConcurrentDictionary PropertyAccessorsCache = new(); + private static readonly ConcurrentDictionary HasImagesAttributeCache = new(); + public static bool IsPrimitiveType(object obj) => + obj is not null && (obj.GetType().IsPrimitive || obj is string or ValueType); + + public static bool IsCollectionType(object obj) => obj is IEnumerable && obj is not string; + + public static object ToDictionaryOrList(object obj) + { + if (obj == null) return null; + + return obj switch + { + _ when IsPrimitiveType(obj) => obj, + IEnumerable enumerable => TransformCollection(enumerable), + _ => TransformObject(obj) + }; + } + + private static List TransformCollection(IEnumerable enumerable) + { + var list = new List(); + foreach (var item in enumerable) + { + list.Add(ToDictionaryOrList(item)); + } + return list; + } + + private static Dictionary TransformObject(object obj) + { + var type = obj.GetType(); + var accessors = PropertyAccessorsCache.GetOrAdd(type, CreatePropertyAccessors); + var keyValuePairs = new Dictionary(accessors.Length); + + foreach (var accessor in accessors) + { + var propertyValue = accessor.Getter(obj); + keyValuePairs[accessor.Name] = IsCollectionType(propertyValue) + ? ToDictionaryOrList(propertyValue) + : propertyValue; + } + + return keyValuePairs; + } + + private static PropertyAccessor[] CreatePropertyAccessors(Type type) + { + var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); + var accessors = new PropertyAccessor[properties.Length]; + + for (int i = 0; i < properties.Length; i++) + { + var property = properties[i]; + //var x = property.GetCustomAttribute() != null; + + var getter = CreateGetter(property); + accessors[i] = new PropertyAccessor(property.Name, getter); + } + + return accessors; + } + + private static Func CreateGetter(PropertyInfo property) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var castInstance = Expression.Convert(parameter, property.DeclaringType); + var propertyAccess = Expression.Property(castInstance, property); + var castResult = Expression.Convert(propertyAccess, typeof(object)); + var lambda = Expression.Lambda>(castResult, parameter); + return lambda.Compile(); + } + + private record PropertyAccessor(string Name, Func Getter); +} + +[Obsolete] +public static class ObjectExtensions5 +{ + // 用于缓存每个类型的属性访问器数组,以减少重复的反射操作 + private static readonly ConcurrentDictionary PropertyCache = new(); + + // 判断对象是否为原始类型、字符串或值类型 + public static bool IsPrimitiveType(object obj) => + obj is not null && (obj.GetType().IsPrimitive || obj is string or ValueType); + + // 判断对象是否为集合类型(非字符串的 IEnumerable) + public static bool IsCollectionType(object obj) => obj is IEnumerable && obj is not string; + + /// + /// 将对象转换为字典或列表结构 + /// + /// + /// + public static object ToDictionaryOrList(object obj) + { + if (obj == null) return null; + + // 根据对象类型执行相应的转换操作 + return obj switch + { + _ when IsPrimitiveType(obj) => obj, // 原始类型直接返回 + IEnumerable enumerable => TransformCollection(enumerable), // 集合类型转换为列表 + _ => TransformObject(obj) // 复杂类型转换为字典 + }; + } + + /// + /// 转换集合类型,将每个元素递归转换为字典或列表 + /// + /// + /// + private static List TransformCollection(IEnumerable enumerable) + { + var list = new List(enumerable is ICollection collection ? collection.Count : 10); + foreach (var item in enumerable) + { + list.Add(ToDictionaryOrList(item)); + } + return list; + } + + // 转换复杂类型为字典结构,键为属性名,值为属性值 + private static Dictionary TransformObject(object obj) + { + var type = obj.GetType(); + + // 从缓存中获取或创建该类型的属性访问器数组 + var accessors = PropertyCache.GetOrAdd(type, t => CreatePropertyAccessors(t)); + var keyValuePairs = new Dictionary(accessors.Length); + + // 遍历每个属性访问器 + foreach (var accessor in accessors) + { + // 使用表达式树生成的委托获取属性值 + var propertyValue = accessor.Getter(obj); + + // 如果属性具有 ImagesAttribute 特性,将其值转换为 "image{propertyValue}" 格式 + if (accessor.HasImagesAttribute) + { + keyValuePairs[accessor.PropertyName] = $"image{propertyValue}"; + } + else + { + // 根据属性值的类型决定是否递归转换 + keyValuePairs[accessor.PropertyName] = IsCollectionType(propertyValue) + ? ToDictionaryOrList(propertyValue) + : propertyValue; + } + } + + return keyValuePairs; + } + + // 创建指定类型的属性访问器数组 + private static PropertyAccessor[] CreatePropertyAccessors(Type type) + { + // 获取类型的公共实例属性 + var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); + var accessors = new List(properties.Length); + + foreach (var property in properties) + { + // 使用表达式树生成委托来获取属性值,避免使用反射 + var parameter = Expression.Parameter(typeof(object), "obj"); // 输入参数 obj + var castParameter = Expression.Convert(parameter, type); // 将参数转换为目标类型 + var propertyAccess = Expression.Property(castParameter, property); // 获取属性值 + var convertPropertyAccess = Expression.Convert(propertyAccess, typeof(object)); // 转换为 object 类型 + var getter = Expression.Lambda>(convertPropertyAccess, parameter).Compile(); + + // 检查属性是否具有 ImagesAttribute 特性 + bool hasImagesAttribute = property.GetCustomAttribute() != null; + + // 将属性名、Getter 委托和特性标识添加到 PropertyAccessor 对象中 + accessors.Add(new PropertyAccessor(property.Name, getter, hasImagesAttribute)); + } + + return accessors.ToArray(); + } + + // 表示属性访问器的类,包含属性名称、Getter 委托和特性标识 + private class PropertyAccessor + { + public string PropertyName { get; } // 属性名称 + public Func Getter { get; } // 委托,用于获取属性值 + public bool HasImagesAttribute { get; } // 是否包含 ImagesAttribute 特性 + + public PropertyAccessor(string propertyName, Func getter, bool hasImagesAttribute) + { + PropertyName = propertyName; + Getter = getter; + HasImagesAttribute = hasImagesAttribute; + } + } +} + +[Obsolete] +public static class ObjectExtensions6 +{ + /// + /// 使用线程安全的字典缓存每个类型的属性访问器 + /// + private static readonly ConcurrentDictionary PropertyCache = new(); + /// + /// 缓存每个属性是否具有 ImagesAttribute 特性 + /// + private static readonly ConcurrentDictionary _PropertyCache = new ConcurrentDictionary(); + + /// + /// 判断对象是否为原始类型或字符串 + /// + /// + /// + public static bool IsPrimitiveType(object obj) => + obj is not null && (obj.GetType().IsPrimitive || obj is string or ValueType); + + /// + /// 判断对象是否为集合类型(但不是字符串) + /// + /// + /// + public static bool IsCollectionType(object obj) => obj is IEnumerable && obj is not string; + + /// + /// 将对象转换为字典或列表 + /// + /// + /// + public static object ToDictionaryOrList(object obj) + { + if (obj == null) return null; + + return obj switch + { + _ when IsPrimitiveType(obj) => obj, + IEnumerable enumerable => TransformCollection(enumerable), + _ => TransformObject(obj) + }; + } + + /// + /// 将集合转换为列表 + /// + /// + /// + private static List TransformCollection(IEnumerable enumerable) + { + // 如果集合实现了 ICollection 接口,则使用其 Count 属性初始化列表容量 + var list = new List(enumerable is ICollection collection ? collection.Count : 10); + foreach (var item in enumerable) + { + list.Add(ToDictionaryOrList(item)); + } + return list; + } + + // 将对象转换为字典 + private static Dictionary TransformObject(object obj) + { + var type = obj.GetType(); + // 获取或创建该类型的属性访问器 + var accessors = PropertyCache.GetOrAdd(type, t => CreatePropertyAccessors(t)); + var keyValuePairs = new Dictionary(accessors.Length); + + foreach (var accessor in accessors) + { + var propertyValue = accessor.Getter(obj); + + if (accessor.HasImagesAttribute) + { + // 如果属性具有 ImagesAttribute 特性,处理方式不同 + keyValuePairs[accessor.PropertyName] = $"image{propertyValue}"; + } + else + { + // 如果属性是集合类型,递归转换 + keyValuePairs[accessor.PropertyName] = IsCollectionType(propertyValue) + ? ToDictionaryOrList(propertyValue) + : propertyValue; + } + } + + return keyValuePairs; + } + + // 创建属性访问器数组 + private static PropertyAccessor[] CreatePropertyAccessors(Type type) + { + var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); + var accessors = new List(properties.Length); + + foreach (var property in properties) + { + 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)); + var getter = Expression.Lambda>(convertPropertyAccess, parameter).Compile(); + + // 使用缓存检查属性是否具有 ImagesAttribute 特性 + bool hasImagesAttribute = GetHasImagesAttribute(property, _PropertyCache); + + accessors.Add(new PropertyAccessor(property.Name, getter, hasImagesAttribute)); + } + + return accessors.ToArray(); + } + + // 检查属性是否具有 ImagesAttribute 特性,并缓存结果 + private static bool GetHasImagesAttribute(PropertyInfo property, ConcurrentDictionary propertyCache) + { + if (propertyCache.TryGetValue(property, out bool hasImagesAttribute)) + { + return hasImagesAttribute; + } + + hasImagesAttribute = property.GetCustomAttribute() != null; + propertyCache[property] = hasImagesAttribute; + + return hasImagesAttribute; + } + + // 属性访问器类,包含属性名、获取器和特性标志 + 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; + } + } +} + +[Obsolete] +public static class ObjectExtensions7 +{ + private static readonly ConcurrentDictionary PropertyCache = new(); + private static readonly ConcurrentDictionary _PropertyCache = new(); + + public static bool IsPrimitiveType(object obj) => + obj is not null && (obj.GetType().IsPrimitive || obj is string or ValueType); + + public static bool IsCollectionType(object obj) => obj is IEnumerable && obj is not string; + + public static object ToDictionaryOrList(object obj) + { + if (obj == null) return null; + + return obj switch + { + _ when IsPrimitiveType(obj) => obj, + IEnumerable enumerable => TransformCollection(enumerable), + _ => TransformObject(obj) + }; + } + + private static List TransformCollection(IEnumerable enumerable) + { + var list = new List(enumerable is ICollection collection ? collection.Count : 10); + foreach (var item in enumerable) + { + list.Add(ToDictionaryOrList(item)); + } + return list; + } + + private static Dictionary TransformObject(object obj) + { + var type = obj.GetType(); + var accessors = PropertyCache.GetOrAdd(type, CreatePropertyAccessors); + var keyValuePairs = new Dictionary(accessors.Length); + + foreach (var accessor in accessors) + { + var propertyValue = accessor.Getter(obj); + keyValuePairs[accessor.PropertyName] = accessor.HasImagesAttribute + ? $"image{propertyValue}" + : IsCollectionType(propertyValue) ? ToDictionaryOrList(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); + bool hasImagesAttribute = _PropertyCache.GetOrAdd(property, p => p.GetCustomAttribute() != null); + return new PropertyAccessor(property.Name, getter, hasImagesAttribute); + }).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; + } + } +} +