Skip to content

Instantly share code, notes, and snippets.

@kfrancis
Created November 26, 2024 20:22
Show Gist options
  • Save kfrancis/429f7c3e70e7a3a468024571cd605841 to your computer and use it in GitHub Desktop.
Save kfrancis/429f7c3e70e7a3a468024571cd605841 to your computer and use it in GitHub Desktop.
Expression/Property fun
public static class EntityCopy
{
public static void UpdateEntity<T>(T source, T destination)
where T : class
{
var properties = PropertyAccessorCache.GetCachedProperties<T>();
foreach (var property in properties.Where(property => property is { CanRead: true, CanWrite: true }))
{
property.SetValue(destination, property.GetValue(source));
}
}
public static T CopyEntity<T>(T source)
where T : class, new()
{
T copy = new();
UpdateEntity(source, copy);
return copy;
}
}
public static class PropertyAccessorCache
{
// Cache for property information per type
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> s_propertyCache = new();
// Cache for getter delegates per (Type, PropertyName)
private static readonly ConcurrentDictionary<PropertyInfo, Func<object, object?>> s_getters = new();
// Cache for setter delegates per (Type, PropertyName)
private static readonly ConcurrentDictionary<PropertyInfo, Action<object, object?>> s_setters = new();
// Get cached properties for a type
public static PropertyInfo[] GetCachedProperties<T>()
{
return s_propertyCache.GetOrAdd(typeof(T), t => t.GetProperties(BindingFlags.Instance | BindingFlags.Public));
}
public static Action<object, object?> GetSetter<T>(PropertyInfo propertyInfo)
{
return s_setters.GetOrAdd(propertyInfo, pi =>
{
var targetType = typeof(T);
var instance = Expression.Parameter(typeof(object), "instance");
var value = Expression.Parameter(typeof(object), "value");
// Convert instance to target type
var instanceCast = Expression.Convert(instance, targetType);
// Convert value to property type
var valueCast = Expression.Convert(value, pi.PropertyType);
// Create the property setter expression
var propertySetter = Expression.Call(instanceCast, pi.GetSetMethod()!, valueCast);
// Compile the expression into a delegate
var lambda = Expression.Lambda<Action<object, object?>>(propertySetter, instance, value);
return lambda.Compile();
});
}
public static Func<object, object?> GetGetter<T>(PropertyInfo propertyInfo)
{
return s_getters.GetOrAdd(propertyInfo, pi =>
{
var targetType = typeof(T);
var instance = Expression.Parameter(typeof(object), "instance");
// Convert instance to target type
var instanceCast = Expression.Convert(instance, targetType);
// Access the property
var propertyAccess = Expression.Property(instanceCast, pi);
// Convert the property value to object (boxing if necessary)
var convertPropertyAccess = Expression.Convert(propertyAccess, typeof(object));
// Compile the expression into a delegate
var lambda = Expression.Lambda<Func<object, object?>>(convertPropertyAccess, instance);
return lambda.Compile();
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment