Last active
March 17, 2018 10:20
-
-
Save sdluxeon/df35bef9d208f1a9d36383ffcf1e6dc8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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