I'm using C#, and I have two approaches for accessing class properties: through Entity<Unit>.PropertyAccessors
and EntityManager.PropertyAccessor<Unit>()
. Both options provide access to class metadata for working with properties. I'm interested in the differences in their performance, memory usage, thread safety, and other relevant aspects.
What are the key distinctions between Entity<Unit>.PropertyAccessors
and EntityManager.PropertyAccessor<Unit>()
in terms of performance, memory usage, thread safety, and other critical aspects? Is there a preferred option in each of these categories? Are there any potential pitfalls or gotchas that I should be aware of when using either of these implementations?
I'm using a ThreadSafeDictionary with ConcurrentDictionary<TKey, Lazy<TValue>>
under the hood.
Below is the code I'm using for a more detailed context:
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();
}
}
Aucun commentaire:
Enregistrer un commentaire