You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中如何通过委托优化反射属性对比的GetValue性能问题

高性能对象属性对比实现方案

原实现的性能损耗来自三个核心点:

  • 每次调用对比方法都重复执行GetProperties反射查询类型元数据
  • 高频调用PropertyInfo.GetValue走反射Invoke逻辑,开销是原生直接调用属性getter的上百倍
  • 存在冗余逻辑:已经拿到PropertyInfo对象的前提下,又重复调用type.GetProperty(pi.Name)二次查询属性,增加无意义开销

优化思路

用类型元数据+取值委托全局缓存方案:

  1. 每个类型的公开实例属性元数据,仅在第一次对比该类型时反射获取一次
  2. 通过表达式树为每个属性预编译强类型取值委托,绕开反射GetValue的调用开销,委托调用性能和手写硬编码取值几乎一致
  3. 自动兼容所有属性类型:包含int、枚举等值类型(自动处理装箱),string、自定义类等引用类型,无需针对特定类型做适配

完整实现代码

using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;

public class ObjectComparer
{
    // 全局缓存:key=待对比类型,value=该类型所有可读属性的取值委托+属性名
    private static readonly ConcurrentDictionary<Type, IReadOnlyList<PropertyCompareEntry>> _propertyCache = new();

    /// <summary>
    /// 属性对比条目缓存
    /// </summary>
    private class PropertyCompareEntry
    {
        public string PropertyName { get; set; }
        public Func<object, object> ValueGetter { get; set; }
    }

    /// <summary>
    /// 获取指定类型的属性对比缓存条目,不存在则构建
    /// </summary>
    private static IReadOnlyList<PropertyCompareEntry> GetPropertyEntries<T>()
    {
        var targetType = typeof(T);
        return _propertyCache.GetOrAdd(targetType, type =>
        {
            // 过滤出所有公有的、可读的、非索引器的实例属性
            var validProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(pi => pi.CanRead && pi.GetIndexParameters().Length == 0);
            var entryList = new List<PropertyCompareEntry>();

            foreach (var prop in validProperties)
            {
                // 构建表达式树:(object instance) => (object)((T)instance).Prop
                var instanceParam = Expression.Parameter(typeof(object), "instance");
                var castedInstance = Expression.Convert(instanceParam, type);
                var propAccess = Expression.Property(castedInstance, prop);
                var boxedValue = Expression.Convert(propAccess, typeof(object)); // 值类型自动装箱
                var valueGetter = Expression.Lambda<Func<object, object>>(boxedValue, instanceParam).Compile();

                entryList.Add(new PropertyCompareEntry
                {
                    PropertyName = prop.Name,
                    ValueGetter = valueGetter
                });
            }

            return entryList;
        });
    }

    /// <summary>
    /// 对比两个同类型对象的属性值,返回值不相等的属性名集合
    /// </summary>
    public List<string> Compare<T>(T A, T B)
    {
        if (A == null) throw new ArgumentNullException(nameof(A));
        if (B == null) throw new ArgumentNullException(nameof(B));

        var propertyEntries = GetPropertyEntries<T>();
        var unequalProps = new List<string>();
        foreach (var entry in propertyEntries)
        {
            object valA = entry.ValueGetter(A);
            object valB = entry.ValueGetter(B);

            // 保持和原逻辑一致的相等判断规则
            if (valA == valB) continue;
            if (valA == null || !valA.Equals(valB))
            {
                unequalProps.Add(entry.PropertyName);
            }
        }

        return unequalProps;
    }
}

性能说明

  • 冷启动(第一次对比某类型)时会一次性完成该类型所有属性的反射和委托编译,耗时略高于原实现
  • 热启动(同类型后续对比)时无任何反射开销,取值性能比原GetValue实现高50~100倍,遍历十万级以上对象列表时不会出现明显性能滞后
  • 自动过滤索引器属性,修复原代码遇到索引器时调用GetValue抛异常的潜在问题
  • 可直接扩展:需要忽略特定属性时,可在构建缓存阶段通过自定义特性、属性名黑名单等方式过滤即可

内容的提问来源于stack exchange,提问作者Vignesh G

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 18:09:37