123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- using System;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Reflection;
- namespace InABox.Core
- {
- public static class Expressions
- {
- #region Property Getter Setter Functions
- // Allows setting of object properties via cached expressions
- public static Func<T, TValue> Getter<T, TValue>(Expression<Func<T, TValue>> expression)
- {
- return expression.Compile();
- }
- public static Func<T, object> Getter<T>(string propname)
- {
- Func<T, object>? result = null;
- try
- {
- var param = Expression.Parameter(typeof(T), "x");
- Expression body = param;
- foreach (var member in propname.Split('.'))
- body = Expression.PropertyOrField(body, member);
- var lambda = Expression.Lambda<Func<T, object>>(body, param);
- result = lambda.Compile();
- }
- catch (Exception e)
- {
- Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
- }
- return result;
- }
- public static Func<object, object> Getter(Type objectType, string propname)
- {
- Func<object, object> result = null;
- try
- {
- var objectParameter = Expression.Parameter(typeof(object), "o");
- var param = Expression.ConvertChecked(objectParameter, objectType);
- Expression body = param;
- foreach (var member in propname.Split('.'))
- body = Expression.PropertyOrField(body, member);
- Expression conversion = Expression.Convert(body, typeof(object));
- var lambda = Expression.Lambda<Func<object, object>>(conversion, objectParameter);
- result = lambda.Compile();
- }
- catch (Exception e)
- {
- Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
- }
- return result;
- }
- public static Func<T, object> Getter<T>(string propname, string key)
- {
- Func<T, object> result = null;
- try
- {
- var param = Expression.Parameter(typeof(T), "o");
- Expression body = param;
- foreach (var member in propname.Split('.'))
- body = Expression.PropertyOrField(body, member);
- Expression keyExpr = Expression.Constant(key, typeof(string)); //Parameter(typeof(string));
- // Alternative, note that we could even look for the type of parameters, if there are indexer overloads.
- var indexer = (from p in body.Type.GetDefaultMembers().OfType<PropertyInfo>()
- // This check is probably useless. You can't overload on return value in C#.
- where p.PropertyType == typeof(object)
- let q = p.GetIndexParameters()
- // Here we can search for the exact overload. Length is the number of "parameters" of the indexer, and then we can check for their type.
- where q.Length == 1 && q[0].ParameterType == typeof(string)
- select p).Single();
- var indexExpr = Expression.Property(body, indexer, keyExpr);
- var lambdaGetter = Expression.Lambda<Func<T, object>>(indexExpr, param);
- result = lambdaGetter.Compile();
- }
- catch (Exception e)
- {
- Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
- }
- return result;
- }
- public static Func<object, object> Getter(Type objectType, string propname, string key)
- {
- Func<object, object> result = null;
- try
- {
- var objectParameter = Expression.Parameter(typeof(object), "o");
- var param = Expression.ConvertChecked(objectParameter, objectType);
- Expression body = param;
- foreach (var member in propname.Split('.'))
- body = Expression.PropertyOrField(body, member);
- Expression keyExpr = Expression.Constant(key, typeof(string)); //Parameter(typeof(string));
- // Alternative, note that we could even look for the type of parameters, if there are indexer overloads.
- var indexer = (from p in body.Type.GetDefaultMembers().OfType<PropertyInfo>()
- // This check is probably useless. You can't overload on return value in C#.
- where p.PropertyType == typeof(object)
- let q = p.GetIndexParameters()
- // Here we can search for the exact overload. Length is the number of "parameters" of the indexer, and then we can check for their type.
- where q.Length == 1 && q[0].ParameterType == typeof(string)
- select p).Single();
- var indexExpr = Expression.Property(body, indexer, keyExpr);
- var lambdaGetter = Expression.Lambda<Func<object, object>>(indexExpr, objectParameter);
- result = lambdaGetter.Compile();
- }
- catch (Exception e)
- {
- Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
- }
- return result;
- }
- public static bool IsWriteable(Expression expression)
- {
- switch (expression.NodeType)
- {
- case ExpressionType.Index:
- return expression is IndexExpression index && index.Indexer is PropertyInfo indexer && indexer.CanWrite;
- case ExpressionType.MemberAccess:
- if(expression is MemberExpression me)
- {
- if (me.Member is PropertyInfo prop)
- return prop.CanWrite;
- else if (me.Member is FieldInfo field)
- return !(field.IsInitOnly || field.IsLiteral);
- }
- return false;
- case ExpressionType.Parameter:
- return true;
- }
- return false;
- }
- public static Action<T, object?> Setter<T>(string propname)
- {
- var param = Expression.Parameter(typeof(T), "o");
- Expression body = param;
- foreach (var member in propname.Split('.'))
- body = Expression.PropertyOrField(body, member);
- var valueParameter = Expression.Parameter(typeof(object), "v");
- var value = Expression.ConvertChecked(valueParameter, body.Type);
- Expression expression;
- if (IsWriteable(body))
- {
- try
- {
- expression = Expression.Assign(body, value);
- }
- catch
- {
- expression = Expression.Empty();
- }
- }
- else
- {
- expression = Expression.Empty();
- }
- var lambda = Expression.Lambda<Action<T, object?>>(expression, param, valueParameter);
- var compiled = lambda.Compile();
- return compiled;
- }
- public static Action<object, object?> Setter(Type objectType, string propname, Type? type = null)
- {
- Action<object, object?>? compiled = null;
- try
- {
- var objectParameter = Expression.Parameter(typeof(object), "o");
- var param = Expression.ConvertChecked(objectParameter, objectType);
- Expression body = param;
- foreach (var member in propname.Split('.'))
- body = Expression.PropertyOrField(body, member);
- var valueParameter = Expression.Parameter(typeof(object), "v");
- UnaryExpression value;
- if (type != null)
- {
- var val1 = Expression.Convert(valueParameter, type);
- value = Expression.ConvertChecked(val1, body.Type);
- }
- else
- {
- value = Expression.ConvertChecked(valueParameter, body.Type);
- }
- Expression expression;
- if (IsWriteable(body))
- {
- try
- {
- expression = Expression.Assign(body, value);
- }
- catch
- {
- expression = Expression.Empty();
- }
- }
- else
- {
- expression = Expression.Empty();
- }
- var lambda = Expression.Lambda<Action<object, object?>>(expression, objectParameter, valueParameter);
- compiled = lambda.Compile();
- }
- catch (Exception e)
- {
- Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
- }
- return compiled;
- }
- public static Action<T, object?> Setter<T>(string propname, string key)
- {
- Action<T, object?> result = null;
- try
- {
- var param = Expression.Parameter(typeof(T), "o");
- Expression body = param;
- foreach (var member in propname.Split('.'))
- body = Expression.PropertyOrField(body, member);
- Expression keyExpr = Expression.Constant(key, typeof(string));
- var valueExpr = Expression.Parameter(typeof(object));
- // Alternative, note that we could even look for the type of parameters, if there are indexer overloads.
- var indexer = (from p in body.Type.GetDefaultMembers().OfType<PropertyInfo>()
- // This check is probably useless. You can't overload on return value in C#.
- where p.PropertyType == typeof(object)
- let q = p.GetIndexParameters()
- // Here we can search for the exact overload. Length is the number of "parameters" of the indexer, and then we can check for their type.
- where q.Length == 1 && q[0].ParameterType == typeof(string)
- select p).Single();
- var indexExpr = Expression.Property(body, indexer, keyExpr);
- var assign = Expression.Assign(indexExpr, valueExpr);
- var lambdaSetter = Expression.Lambda<Action<T, object?>>(assign, param, valueExpr);
- result = lambdaSetter.Compile();
- }
- catch (Exception e)
- {
- Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
- }
- return result;
- }
- public static Action<object, object?> Setter(Type objectType, string propname, string key)
- {
- Action<object, object?> result = null;
- try
- {
- var objectParameter = Expression.Parameter(typeof(object), "o");
- var param = Expression.ConvertChecked(objectParameter, objectType);
- Expression body = param;
- foreach (var member in propname.Split('.'))
- body = Expression.PropertyOrField(body, member);
- Expression keyExpr = Expression.Constant(key, typeof(string));
- var valueExpr = Expression.Parameter(typeof(object));
- // Alternative, note that we could even look for the type of parameters, if there are indexer overloads.
- var indexer = (from p in body.Type.GetDefaultMembers().OfType<PropertyInfo>()
- // This check is probably useless. You can't overload on return value in C#.
- where p.PropertyType == typeof(object)
- let q = p.GetIndexParameters()
- // Here we can search for the exact overload. Length is the number of "parameters" of the indexer, and then we can check for their type.
- where q.Length == 1 && q[0].ParameterType == typeof(string)
- select p).Single();
- var indexExpr = Expression.Property(body, indexer, keyExpr);
- var assign = Expression.Assign(indexExpr, valueExpr);
- var lambdaSetter = Expression.Lambda<Action<object, object?>>(assign, objectParameter, valueExpr);
- result = lambdaSetter.Compile();
- }
- catch (Exception e)
- {
- Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
- }
- return result;
- }
- #endregion
- }
- }
|