.NET MAUI Android反射性能问题:DTO属性提取优化求助
优化.NET MAUI Android端反射性能方案
问题背景
拥有多个包含100+属性的DTO类,属性带有DisplayData、Section等自定义特性;从API反序列化为DTO后,通过反射提取属性数据生成DTOPropertyInfo集合。该操作在iOS设备上流畅,但在新旧Android设备上均存在明显卡顿,且不想手动编写每个属性的处理逻辑。
核心优化方案
1. 缓存反射元数据,避免重复反射调用
反射中GetType().GetProperties()、GetCustomAttribute是高开销操作,可通过静态字典缓存每个DTO类型的属性与对应特性信息,避免每次处理都重复反射:
private static readonly Dictionary<Type, List<(PropertyInfo Prop, DisplayDataAttribute Display, SectionAttribute Section)>> _propertyCache = new(); private List<(PropertyInfo Prop, DisplayDataAttribute Display, SectionAttribute Section)> GetCachedProperties(Type dtoType) { if (_propertyCache.TryGetValue(dtoType, out var cached)) return cached; var properties = dtoType.GetProperties() .Select(p => { var display = p.GetCustomAttribute<DisplayDataAttribute>(); if (display == null) return null; var section = p.GetCustomAttribute<SectionAttribute>() ?? dtoType.GetCustomAttribute<SectionAttribute>(); return (Prop: p, Display: display, Section: section); }) .Where(item => item != null && item.Section != null) .ToList(); _propertyCache[dtoType] = properties; return properties; }
后续处理同一类型DTO时,直接从缓存读取属性与特性,无需重复反射。
2. 用表达式树替代PropertyInfo.GetValue
PropertyInfo.GetValue的反射调用性能较差,可通过表达式树编译成委托,将属性读取速度提升至接近直接调用属性的水平:
private static readonly Dictionary<Type, List<(Func<object, object> GetValue, DisplayDataAttribute Display, SectionAttribute Section)>> _propertyAccessorCache = new(); private List<(Func<object, object> GetValue, DisplayDataAttribute Display, SectionAttribute Section)> GetCachedAccessors(Type dtoType) { if (_propertyAccessorCache.TryGetValue(dtoType, out var cached)) return cached; var accessors = new List<(Func<object, object>, DisplayDataAttribute, SectionAttribute)>(); var properties = dtoType.GetProperties(); foreach (var prop in properties) { var display = prop.GetCustomAttribute<DisplayDataAttribute>(); if (display == null) continue; var section = prop.GetCustomAttribute<SectionAttribute>() ?? dtoType.GetCustomAttribute<SectionAttribute>(); if (section == null) continue; // 编译属性取值委托 var param = Expression.Parameter(typeof(object)); var castParam = Expression.Convert(param, dtoType); var propertyAccess = Expression.Property(castParam, prop); var castResult = Expression.Convert(propertyAccess, typeof(object)); var getter = Expression.Lambda<Func<object, object>>(castResult, param).Compile(); accessors.Add((getter, display, section)); } _propertyAccessorCache[dtoType] = accessors; return accessors; }
使用时直接调用委托getter(dto)获取属性值,替代prop.GetValue(dto)。
3. 批量处理异步操作,减少上下文切换
原代码在循环内频繁调用await storage.GetAsync,会导致多次异步等待叠加延迟。可先收集所有需要查询的key,通过Task.WhenAll批量执行异步请求:
if (type is not QueryType.None) { var accessors = GetCachedAccessors(dto.GetType()); // 收集所有需要查询的键 var favoriteKeys = accessors.Select(a => $"{type}/{a.Display.DisplayName}").ToList(); // 批量执行异步查询 var favoriteTasks = favoriteKeys.Select(k => storage.GetAsync(k)).ToList(); var favoriteValues = await Task.WhenAll(favoriteTasks); for (int i = 0; i < accessors.Count; i++) { var accessor = accessors[i]; var favorite = favoriteValues[i]; if (!string.IsNullOrEmpty(favorite)) { favorites.Add(new() { DisplayName = favorite, Value = accessor.GetValue(dto).FormatDisplayValue(), IsFavorite = true, Section = accessor.Section.Section, Unit = accessor.Display.Unit }); continue; } // 处理非收藏属性逻辑 } }
4. 减少不必要的类型判断与转换
- 在缓存阶段提前标记属性类型(如是否为
BaseDTO[]、string[]),避免每次运行时重复判断; - 若属性类型确定为
string,直接调用FormatDisplayValue,无需value as string的类型转换判断。
Android平台专属优化
- 开启AOT编译:在Android项目的csproj中添加配置,提前编译反射相关代码,大幅提升Android设备运行性能:
<PropertyGroup> <PublishAot>true</PublishAot> </PropertyGroup> - 启用部分剪裁:配合AOT开启剪裁模式,移除未使用代码,减少内存占用与运行时开销:
<PropertyGroup> <TrimMode>partial</TrimMode> </PropertyGroup> - 后台线程执行反射:将反射处理逻辑放在后台线程,避免阻塞UI线程导致卡顿:
var propertyInfo = await Task.Run(() => GetPropertiesInfoAsync(dto));
内容的提问来源于stack exchange,提问作者Zita
相关产品推荐
相关产品推荐

