Skip to content

Instantly share code, notes, and snippets.

@sdluxeon
Last active March 17, 2018 10:20
Show Gist options
  • Select an option

  • Save sdluxeon/df35bef9d208f1a9d36383ffcf1e6dc8 to your computer and use it in GitHub Desktop.

Select an option

Save sdluxeon/df35bef9d208f1a9d36383ffcf1e6dc8 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace ReflectionExpressions
{
public static class ReflectionExpressions
{
public static IPropertyAccess AccessProperty(Type objectType, string propertyName)
{
var property = objectType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).SingleOrDefault(x => x.Name == propertyName);
return AccessProperty(objectType, property);
}
public static IPropertyAccess AccessProperty(Type objectType, PropertyInfo property)
{
var genericMemberAccessType = typeof(GenericMemberAccess<,>);
var memberAccessType = genericMemberAccessType.MakeGenericType(objectType, property.PropertyType);
return Activator.CreateInstance(memberAccessType, new object[] { property }) as IPropertyAccess;
}
public static IObjectFactory CreateFactory(Type objectType)
{
var factory = new ObjectFactory(objectType);
return factory;
}
}
public class ObjectFactory : IObjectFactory
{
private ConstructorRegistry registry;
private Dictionary<ConstructorInfo, ParameterInfo[]> constructorInfo;
public ObjectFactory(Type type)
{
registry = new ConstructorRegistry();
constructorInfo = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).ToDictionary(x => x, x => x.GetParameters());
foreach (var item in constructorInfo)
{
var ctorDelegate = BuildConstructor(item.Key);
registry.AddConstructor(ctorDelegate, item.Value);
}
}
public delegate object ConstructorDelegate(params object[] args);
public ConstructorDelegate BuildConstructor(ConstructorInfo ctor)
{
ParameterInfo[] parameters = constructorInfo[ctor];
ParameterExpression param = Expression.Parameter(typeof(object[]), "args");
Expression[] argumentExpression = new Expression[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
{
Expression index = Expression.Constant(i);
Type paramType = parameters[i].ParameterType;
Expression paramAccessorExp = Expression.ArrayIndex(param, index);
Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType);
argumentExpression[i] = paramCastExp;
}
NewExpression constructorExpression = Expression.New(ctor, argumentExpression);
LambdaExpression lambda = Expression.Lambda(typeof(ConstructorDelegate), constructorExpression, param);
return (ConstructorDelegate)lambda.Compile();
}
public object CreateInstance(params object[] args)
{
return registry.LocateDelegate(args).Invoke(args);
}
private class ConstructorRegistry
{
Dictionary<int, ConstructorDelegate> firstStopDelegates;
Dictionary<int, Dictionary<ConstructorDelegate, ParameterInfo[]>> allDelegates;
public ConstructorRegistry()
{
firstStopDelegates = new Dictionary<int, ConstructorDelegate>();
allDelegates = new Dictionary<int, Dictionary<ConstructorDelegate, ParameterInfo[]>>();
}
public void AddConstructor(ConstructorDelegate @delegate, ParameterInfo[] parameters)
{
if (firstStopDelegates.ContainsKey(parameters.Length))
firstStopDelegates.Remove(parameters.Length);
else
firstStopDelegates.Add(parameters.Length, @delegate);
if (!allDelegates.ContainsKey(parameters.Length))
allDelegates.Add(parameters.Length, new Dictionary<ConstructorDelegate, ParameterInfo[]>());
allDelegates[parameters.Length].Add(@delegate, parameters);
}
public ConstructorDelegate LocateDelegate(params object[] args)
{
ConstructorDelegate del;
if (!firstStopDelegates.TryGetValue(args.Length, out del))
{
foreach (var ctor in allDelegates[args.Length])
{
bool match = true;
for (int i = 0; i < args.Length; i++)
{
var parameter = ctor.Value[i];
if (!parameter.ParameterType.IsAssignableFrom(args[i].GetType()))
{
match = false;
break;
}
}
if (match)
{
return ctor.Key;
}
}
throw new MemberAccessException("Could not find contructor that match the arguments");
}
return del;
}
}
}
public interface IObjectFactory
{
object CreateInstance(params object[] parameters);
}
public interface IPropertyAccess
{
object GetValue(object instance);
void SetValue(object instance, object value);
}
public class GenericMemberAccess<TObject, TProperty> : IPropertyAccess
{
private Func<TObject, TProperty> getter;
private Action<TObject, TProperty> setter;
public GenericMemberAccess(string propertyName)
{
getter = CreatePropertyGetter(propertyName);
setter = CreatePropertySetter(propertyName);
}
public GenericMemberAccess(PropertyInfo property)
{
getter = CreatePropertyGetter(property);
setter = CreatePropertySetter(property);
}
public static Func<TObject, TProperty> CreatePropertyGetter(PropertyInfo property)
{
ParameterExpression paramExpression = Expression.Parameter(typeof(TObject), "value");
Expression propertyGetterExpression = Expression.Property(paramExpression, property);
Func<TObject, TProperty> result =
Expression.Lambda<Func<TObject, TProperty>>(propertyGetterExpression, paramExpression).Compile();
return result;
}
public static Action<TObject, TProperty> CreatePropertySetter(PropertyInfo property)
{
ParameterExpression paramExpression = Expression.Parameter(typeof(TObject));
ParameterExpression paramExpression2 = Expression.Parameter(typeof(TProperty), property.Name);
MemberExpression propertyGetterExpression = Expression.Property(paramExpression, property);
Action<TObject, TProperty> result = Expression.Lambda<Action<TObject, TProperty>>
(
Expression.Assign(propertyGetterExpression, paramExpression2), paramExpression, paramExpression2
).Compile();
return result;
}
public static Func<TObject, TProperty> CreatePropertyGetter(string propertyName)
{
ParameterExpression paramExpression = Expression.Parameter(typeof(TObject), "value");
Expression propertyGetterExpression = Expression.Property(paramExpression, propertyName);
Func<TObject, TProperty> result =
Expression.Lambda<Func<TObject, TProperty>>(propertyGetterExpression, paramExpression).Compile();
return result;
}
public static Action<TObject, TProperty> CreatePropertySetter(string propertyName)
{
ParameterExpression paramExpression = Expression.Parameter(typeof(TObject));
ParameterExpression paramExpression2 = Expression.Parameter(typeof(TProperty), propertyName);
MemberExpression propertyGetterExpression = Expression.Property(paramExpression, propertyName);
Action<TObject, TProperty> result = Expression.Lambda<Action<TObject, TProperty>>
(
Expression.Assign(propertyGetterExpression, paramExpression2), paramExpression, paramExpression2
).Compile();
return result;
}
public object GetValue(object instance)
{
return getter((TObject)instance);
}
public void SetValue(object instance, object value)
{
setter((TObject)instance, (TProperty)value);
}
}
public class ObjectGraph
{
readonly Type type;
readonly IObjectFactory factory;
public ObjectGraph(Type type, IObjectFactory factory)
{
this.factory = factory;
this.type = type;
Properties = new List<ObjectGraphProperty>();
}
public List<ObjectGraphProperty> Properties { get; private set; }
}
public class ObjectGraphProperty
{
public ObjectGraphProperty(int id, Type propertyType, IPropertyAccess property)
{
Id = id;
PropertyType = propertyType;
Property = property;
}
public int Id { get; private set; }
public IPropertyAccess Property { get; private set; }
public Type PropertyType { get; private set; }
}
public class TypeDisocvery
{
public ObjectGraph Inspect(object instance)
{
var type = instance.GetType();
var factory = Expressions.CreateFactory(type);
var gprah = new ObjectGraph(type, factory);
var props = type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
foreach (var item in props)
{
var member = item.GetCustomAttributes(false).Where(x => x is DataMemberAttribute).Cast<DataMemberAttribute>().SingleOrDefault();
if (member != null)
{
var propertyAccess = Expressions.AccessProperty(typeof(User), item.Name);
gprah.Properties.Add(new ObjectGraphProperty(member.Order, item.PropertyType, propertyAccess));
}
}
return gprah;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment