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

namespace Dpz.Core.Entity.Base;

public static class PropertyAccessor
{
    private static readonly ConcurrentDictionary<
        Type,
        ConcurrentDictionary<string, PropertyInfo?>
    > PropertiesKeysCache = new();

    private static readonly ConcurrentDictionary<Type, PropertyInfo[]> PropertiesCache = new();

    private readonly record struct TypePropertyKey(Type Type, string PropertyName);

    private static readonly ConcurrentDictionary<
        TypePropertyKey,
        PropertyInfo?
    > PropertyByNameCache = new();

    public static PropertyInfo[] GetProperties(Type type)
    {
        return PropertiesCache.GetOrAdd(
            type,
            t => t.GetProperties(BindingFlags.Public | BindingFlags.Instance)
        );
    }

    public static PropertyInfo? GetProperty(Type? type, string propertyName)
    {
        if (type == null)
        {
            return null;
        }

        return PropertyByNameCache.GetOrAdd(
            new TypePropertyKey(type, propertyName),
            key =>
                key.Type.GetProperty(key.PropertyName, BindingFlags.Public | BindingFlags.Instance)
        );
    }

    private static class MemberAccessor<T>
    {
        internal static readonly ConcurrentDictionary<
            string,
            Lazy<Action<T, object?>>
        > SetterCache = new();
        internal static readonly ConcurrentDictionary<string, Lazy<Func<T, object?>>> GetterCache =
            new();
    }

    public static Action<T, object?>? GetSetter<T>(string propertyName)
    {
        var property = GetProperty<T>(propertyName);
        if (property == null || !property.CanWrite)
        {
            return null;
        }

        if (MemberAccessor<T>.SetterCache.TryGetValue(propertyName, out var cacheValue))
        {
            return cacheValue.Value;
        }

        var lazyValue = MemberAccessor<T>.SetterCache.GetOrAdd(
            propertyName,
            _ => new Lazy<Action<T, object?>>(
                () => CreateSetterExpressionTree<T>(property),
                LazyThreadSafetyMode.ExecutionAndPublication
            )
        );

        return lazyValue.Value;
    }

    public static Func<T, object?>? GetGetter<T>(string propertyName)
    {
        var property = GetProperty<T>(propertyName);
        if (property == null || !property.CanRead)
        {
            return null;
        }

        if (MemberAccessor<T>.GetterCache.TryGetValue(propertyName, out var cacheValue))
        {
            return cacheValue.Value;
        }

        var lazyValue = MemberAccessor<T>.GetterCache.GetOrAdd(
            propertyName,
            _ => new Lazy<Func<T, object?>>(
                () => CreateGetterExpressionTree<T>(property),
                LazyThreadSafetyMode.ExecutionAndPublication
            )
        );

        return lazyValue.Value;
    }

    private static Action<T, object?> CreateSetterExpressionTree<T>(PropertyInfo property)
    {
        var target = Expression.Parameter(typeof(T), "__q");
        var value = Expression.Parameter(typeof(object), "__q2");
        var convert = Expression.Convert(value, property.PropertyType);
        var propertyAccess = Expression.Property(target, property);
        var assign = Expression.Assign(propertyAccess, convert);
        var lambda = Expression.Lambda<Action<T, object?>>(assign, target, value);
        return lambda.Compile();
    }

    private static Func<T, object?> CreateGetterExpressionTree<T>(PropertyInfo property)
    {
        var target = Expression.Parameter(typeof(T), "__q");
        var propertyAccess = Expression.Property(target, property);
        var convert = Expression.Convert(propertyAccess, typeof(object));
        var lambda = Expression.Lambda<Func<T, object?>>(convert, target);
        return lambda.Compile();
    }

    private static PropertyInfo? GetProperty<T>(string propertyName)
    {
        var typeProperties = PropertiesKeysCache.GetOrAdd(
            typeof(T),
            _ => new ConcurrentDictionary<string, PropertyInfo?>()
        );

        if (typeProperties.TryGetValue(propertyName, out var propertyInfo))
        {
            return propertyInfo;
        }

        var property = GetProperties(typeof(T)).FirstOrDefault(p => p.Name == propertyName);
        typeProperties.TryAdd(propertyName, property);

        return property;
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

这是一个高性能的属性访问器工具类,主要用于通过反射动态访问对象的属性,并使用多种缓存机制来优化性能。让我详细解释一下:

核心功能

这个类提供了以下主要功能:

  1. 获取类型的所有属性
  2. 根据属性名获取特定属性
  3. 动态生成属性的Setter和Getter方法

缓存机制

代码使用了多层缓存来提升性能:

// 1. 缓存类型的所有属性
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> PropertiesCache = new();

// 2. 缓存类型-属性名映射
private static readonly ConcurrentDictionary<Type, ConcurrentDictionary<string, PropertyInfo?>> PropertiesKeysCache = new();

// 3. 缓存单个属性查询
private static readonly ConcurrentDictionary<TypePropertyKey, PropertyInfo?> PropertyByNameCache = new();

// 4. 为每个泛型类型T缓存Setter和Getter
private static class MemberAccessor<T>
{
    internal static readonly ConcurrentDictionary<string, Lazy<Action<T, object?>>> SetterCache = new();
    internal static readonly ConcurrentDictionary<string, Lazy<Func<T, object?>>> GetterCache = new();
}

主要方法解析

1. GetProperties

public static PropertyInfo[] GetProperties(Type type)

获取类型的所有公共实例属性,结果会被缓存。

2. GetProperty

public static PropertyInfo? GetProperty(Type? type, string propertyName)

根据类型和属性名获取特定属性,使用TypePropertyKey作为缓存键。

3. GetSetter/GetGetter

public static Action<T, object?>? GetSetter<T>(string propertyName)
public static Func<T, object?>? GetGetter<T>(string propertyName)

这两个方法是核心功能,它们:

  • 首先检查属性是否存在且可写/可读
  • 使用Lazy<T>确保线程安全的延迟初始化
  • 通过表达式树动态生成高性能的访问器

表达式树生成

Setter生成:

private static Action<T, object?> CreateSetterExpressionTree<T>(PropertyInfo property)
{
    var target = Expression.Parameter(typeof(T), "__q");        // 目标对象参数
    var value = Expression.Parameter(typeof(object), "__q2");   // 值参数
    var convert = Expression.Convert(value, property.PropertyType); // 类型转换
    var propertyAccess = Expression.Property(target, property); // 属性访问
    var assign = Expression.Assign(propertyAccess, convert);    // 赋值操作
    var lambda = Expression.Lambda<Action<T, object?>>(assign, target, value);
    return lambda.Compile(); // 编译为委托
}

Getter生成:

private static Func<T, object?> CreateGetterExpressionTree<T>(PropertyInfo property)
{
    var target = Expression.Parameter(typeof(T), "__q");        // 目标对象参数
    var propertyAccess = Expression.Property(target, property); // 属性访问
    var convert = Expression.Convert(propertyAccess, typeof(object)); // 转换为object
    var lambda = Expression.Lambda<Func<T, object?>>(convert, target);
    return lambda.Compile(); // 编译为委托
}

设计优势

  1. 高性能:使用表达式树生成的委托比直接反射快很多
  2. 线程安全:使用ConcurrentDictionaryLazy<T>确保多线程环境下的安全性
  3. 内存效率:多层缓存避免重复计算和对象创建
  4. 类型安全:泛型设计提供编译时类型检查

使用场景

这个工具类通常用于:

  • ORM框架中的对象属性映射
  • 序列化/反序列化
  • 动态对象操作
  • 数据绑定场景

总的来说,这是一个设计精良的高性能反射工具类,通过多层缓存和表达式树技术,在保持灵活性的同时最大化了性能。

评论加载中...