比较在 C# 中通过静态泛型类和使用 ConcurrentDictionary 访问静态类的特征

Comparing characteristics of accessing class properties in C# via a static generic class and static class with ConcurrentDictionary

提问人:Christopher Fontaine 提问时间:11/10/2023 最后编辑:Christopher Fontaine 更新时间:11/10/2023 访问量:76

问:

我使用的是 C#,我有两种访问类属性的方法:through 和 .这两个选项都提供对类元数据的访问,以便使用属性。我对它们在性能、内存使用、线程安全和其他相关方面的差异很感兴趣。Entity<Unit>.PropertyAccessorsEntityManager.PropertyAccessor<Unit>()

在性能、内存使用、线程安全和其他关键方面,它们之间的主要区别是什么?这些类别中是否有首选选项?在使用这些实现时,我是否应该注意任何潜在的陷阱或陷阱?Entity<Unit>.PropertyAccessorsEntityManager.PropertyAccessor<Unit>()

我正在使用引擎盖下的 ThreadSafeDictionary。ConcurrentDictionary<TKey, Lazy<TValue>>

以下是我用于更详细上下文的代码:

using System.Reflection;
using System.Linq.Expressions;

internal sealed record PropertyAccessor(string Name, Func<object, object> Get, Action<object, object> Set);

internal static class Entity<TEntity> where TEntity : class, IEntity
{
    public static IEnumerable<PropertyAccessor> PropertyAccessors => LazyPropertyAccessors.Value;

    private static readonly Lazy<IList<PropertyAccessor>> LazyPropertyAccessors = new(() => GetPropertiesAccessor(typeof(TEntity)));

    private static IList<PropertyAccessor> GetPropertiesAccessor(IReflect type)
    {
        return type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .Where(p => p.CanRead && p.CanWrite)
            .Select(GetEntityProperty)
            .ToArray();
    }

    private static PropertyAccessor GetEntityProperty(PropertyInfo property)
    {
        return new PropertyAccessor(property.Name, property.GetGetMethod().BuildGetAccessor(), property.GetSetMethod().BuildSetAccessor());
    }
}

internal static class EntityManager
{
    private static readonly IThreadSafeDictionary<Type, IEnumerable<PropertyAccessor>> EntitiesPropertiesAccessor;

    static EntityManager()
    {
        EntitiesPropertiesAccessor = new ThreadSafeDictionary<Type, IEnumerable<PropertyAccessor>>();
    }

    public static IEnumerable<PropertyAccessor> PropertiesAccessor<TEntity>() where TEntity : class, IEntity
    {
        return EntitiesPropertiesAccessor.GetOrAdd(typeof(TEntity), GetPropertiesAccessor);
    }

    private static IEnumerable<PropertyAccessor> GetPropertiesAccessor(IReflect type)
    {
        return type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .Where(p => p.CanRead && p.CanWrite)
            .Select(GetEntityProperty)
            .ToArray();
    }

    private static PropertyAccessor GetEntityProperty(PropertyInfo property)
    {
        return new PropertyAccessor(property.Name, property.GetGetMethod().BuildGetAccessor(), property.GetSetMethod().BuildSetAccessor());
    }
}


public static class ReflectionTypeExtensions
{
    public static Func<object, object> BuildGetAccessor(this MethodInfo method)
    {
        if (method == null)
            throw new ArgumentNullException(nameof(method));

        var declaringType = method.DeclaringType;

        if (declaringType == null)
            throw new NullReferenceException(nameof(method.DeclaringType));

        var obj = Expression.Parameter(typeof(object));

        var getter = Expression.Lambda<Func<object, object>>(
            Expression.Convert(Expression.Call(Expression.Convert(obj, declaringType), method), typeof(object)),
            obj);

        return getter.Compile();
    }

    public static Action<object, object> BuildSetAccessor(this MethodInfo method)
    {
        if (method == null)
            throw new ArgumentNullException(nameof(method));

        var declaringType = method.DeclaringType;

        if (declaringType == null)
            throw new NullReferenceException(nameof(method.DeclaringType));

        var obj = Expression.Parameter(typeof(object));
        var value = Expression.Parameter(typeof(object));

        var expr = Expression.Lambda<Action<object, object>>(Expression.Call(Expression.Convert(obj, declaringType),
                method,
                Expression.Convert(value, method.GetParameters()[0].ParameterType)),
            obj,
            value);

        return expr.Compile();
    }
}

附加信息:

  1. 我还没有尝试过 BenchmarkDotNet。
  2. 我对如何处理静态共享类的内存分配很感兴趣。与具有静态 ConcurrentDictionary 的静态类相比,此方法是否具有优势?
  3. IEntity只是数据库中实体模型的标签

这是他的代码:

public interface IEntity
{
    long Id { get; set; }
}

Unit只是数据库中没有逻辑且仅由属性组成的实体模型 这是他的代码:

public class Unit : IEntity
{
    public long Id { get; set; }

    public string Name { get; set; }

    public string Description { get; set; }
}
  1. 此代码旨在获取实体模型的所有属性,以便以后可以实现复制。添加了 static、Lazy 和 ConcurrentDictionary 机制,以避免每次都对相同的类型使用反射和表达式树。

因此,我将缩短我的问题,如何处理静态泛型类的内存处理?在这种情况下,使用静态缓冲区的一个静态类会更有优势吗?使用静态泛型类时,是否应注意任何潜在的陷阱或问题?

C# 多线程泛 反射 ConcurrentDictionary

评论

1赞 Guru Stron 11/10/2023
您是否尝试过运行它并使用 BenchmarkDotNet 之类的东西?
1赞 MakePeaceGreatAgain 11/10/2023
对于这样一个笼统的问题,不可能提供一个简单的答案。请具体说明您的顾虑,并尝试直接在您的真实应用中进行衡量。很有可能你只是在优化一些东西,而不需要这样做。
1赞 JonasH 11/10/2023
你到底在做什么?这看起来像是涉及反思的事情,我希望这是一些罕见的、高度专业化的案例。如果是这样,你真的关心性能吗?静态属性通常是线程安全的,但请务必检查文档以确保。在实体框架中,有时有不同的方法可以访问同一事物,所以我不会期望有太大的区别,但如果你想确定,请进行基准测试。

答: 暂无答案