Expressions.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. using System;
  2. using System.Linq;
  3. using System.Linq.Expressions;
  4. using System.Reflection;
  5. namespace InABox.Core
  6. {
  7. public static class Expressions
  8. {
  9. #region Property Getter Setter Functions
  10. // Allows setting of object properties via cached expressions
  11. public static Func<T, TValue> Getter<T, TValue>(Expression<Func<T, TValue>> expression)
  12. {
  13. return expression.Compile();
  14. }
  15. public static Func<T, object> Getter<T>(string propname)
  16. {
  17. Func<T, object>? result = null;
  18. try
  19. {
  20. var param = Expression.Parameter(typeof(T), "x");
  21. Expression body = param;
  22. foreach (var member in propname.Split('.'))
  23. body = Expression.PropertyOrField(body, member);
  24. var lambda = Expression.Lambda<Func<T, object>>(body, param);
  25. result = lambda.Compile();
  26. }
  27. catch (Exception e)
  28. {
  29. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  30. }
  31. return result;
  32. }
  33. public static Func<object, object> Getter(Type objectType, string propname)
  34. {
  35. Func<object, object> result = null;
  36. try
  37. {
  38. var objectParameter = Expression.Parameter(typeof(object), "o");
  39. var param = Expression.ConvertChecked(objectParameter, objectType);
  40. Expression body = param;
  41. foreach (var member in propname.Split('.'))
  42. body = Expression.PropertyOrField(body, member);
  43. Expression conversion = Expression.Convert(body, typeof(object));
  44. var lambda = Expression.Lambda<Func<object, object>>(conversion, objectParameter);
  45. result = lambda.Compile();
  46. }
  47. catch (Exception e)
  48. {
  49. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  50. }
  51. return result;
  52. }
  53. public static Func<T, object> Getter<T>(string propname, string key)
  54. {
  55. Func<T, object> result = null;
  56. try
  57. {
  58. var param = Expression.Parameter(typeof(T), "o");
  59. Expression body = param;
  60. foreach (var member in propname.Split('.'))
  61. body = Expression.PropertyOrField(body, member);
  62. Expression keyExpr = Expression.Constant(key, typeof(string)); //Parameter(typeof(string));
  63. // Alternative, note that we could even look for the type of parameters, if there are indexer overloads.
  64. var indexer = (from p in body.Type.GetDefaultMembers().OfType<PropertyInfo>()
  65. // This check is probably useless. You can't overload on return value in C#.
  66. where p.PropertyType == typeof(object)
  67. let q = p.GetIndexParameters()
  68. // 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.
  69. where q.Length == 1 && q[0].ParameterType == typeof(string)
  70. select p).Single();
  71. var indexExpr = Expression.Property(body, indexer, keyExpr);
  72. var lambdaGetter = Expression.Lambda<Func<T, object>>(indexExpr, param);
  73. result = lambdaGetter.Compile();
  74. }
  75. catch (Exception e)
  76. {
  77. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  78. }
  79. return result;
  80. }
  81. public static Func<object, object> Getter(Type objectType, string propname, string key)
  82. {
  83. Func<object, object> result = null;
  84. try
  85. {
  86. var objectParameter = Expression.Parameter(typeof(object), "o");
  87. var param = Expression.ConvertChecked(objectParameter, objectType);
  88. Expression body = param;
  89. foreach (var member in propname.Split('.'))
  90. body = Expression.PropertyOrField(body, member);
  91. Expression keyExpr = Expression.Constant(key, typeof(string)); //Parameter(typeof(string));
  92. // Alternative, note that we could even look for the type of parameters, if there are indexer overloads.
  93. var indexer = (from p in body.Type.GetDefaultMembers().OfType<PropertyInfo>()
  94. // This check is probably useless. You can't overload on return value in C#.
  95. where p.PropertyType == typeof(object)
  96. let q = p.GetIndexParameters()
  97. // 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.
  98. where q.Length == 1 && q[0].ParameterType == typeof(string)
  99. select p).Single();
  100. var indexExpr = Expression.Property(body, indexer, keyExpr);
  101. var lambdaGetter = Expression.Lambda<Func<object, object>>(indexExpr, objectParameter);
  102. result = lambdaGetter.Compile();
  103. }
  104. catch (Exception e)
  105. {
  106. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  107. }
  108. return result;
  109. }
  110. public static bool IsWriteable(Expression expression)
  111. {
  112. switch (expression.NodeType)
  113. {
  114. case ExpressionType.Index:
  115. return expression is IndexExpression index && index.Indexer is PropertyInfo indexer && indexer.CanWrite;
  116. case ExpressionType.MemberAccess:
  117. if(expression is MemberExpression me)
  118. {
  119. if (me.Member is PropertyInfo prop)
  120. return prop.CanWrite;
  121. else if (me.Member is FieldInfo field)
  122. return !(field.IsInitOnly || field.IsLiteral);
  123. }
  124. return false;
  125. case ExpressionType.Parameter:
  126. return true;
  127. }
  128. return false;
  129. }
  130. public static Action<T, object?> Setter<T>(string propname)
  131. {
  132. var param = Expression.Parameter(typeof(T), "o");
  133. Expression body = param;
  134. foreach (var member in propname.Split('.'))
  135. body = Expression.PropertyOrField(body, member);
  136. var valueParameter = Expression.Parameter(typeof(object), "v");
  137. var value = Expression.ConvertChecked(valueParameter, body.Type);
  138. Expression expression;
  139. if (IsWriteable(body))
  140. {
  141. try
  142. {
  143. expression = Expression.Assign(body, value);
  144. }
  145. catch
  146. {
  147. expression = Expression.Empty();
  148. }
  149. }
  150. else
  151. {
  152. expression = Expression.Empty();
  153. }
  154. var lambda = Expression.Lambda<Action<T, object?>>(expression, param, valueParameter);
  155. var compiled = lambda.Compile();
  156. return compiled;
  157. }
  158. public static Action<object, object?> Setter(Type objectType, string propname, Type? type = null)
  159. {
  160. Action<object, object?>? compiled = null;
  161. try
  162. {
  163. var objectParameter = Expression.Parameter(typeof(object), "o");
  164. var param = Expression.ConvertChecked(objectParameter, objectType);
  165. Expression body = param;
  166. foreach (var member in propname.Split('.'))
  167. body = Expression.PropertyOrField(body, member);
  168. var valueParameter = Expression.Parameter(typeof(object), "v");
  169. UnaryExpression value;
  170. if (type != null)
  171. {
  172. var val1 = Expression.Convert(valueParameter, type);
  173. value = Expression.ConvertChecked(val1, body.Type);
  174. }
  175. else
  176. {
  177. value = Expression.ConvertChecked(valueParameter, body.Type);
  178. }
  179. Expression expression;
  180. if (IsWriteable(body))
  181. {
  182. try
  183. {
  184. expression = Expression.Assign(body, value);
  185. }
  186. catch
  187. {
  188. expression = Expression.Empty();
  189. }
  190. }
  191. else
  192. {
  193. expression = Expression.Empty();
  194. }
  195. var lambda = Expression.Lambda<Action<object, object?>>(expression, objectParameter, valueParameter);
  196. compiled = lambda.Compile();
  197. }
  198. catch (Exception e)
  199. {
  200. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  201. }
  202. return compiled;
  203. }
  204. public static Action<T, object?> Setter<T>(string propname, string key)
  205. {
  206. Action<T, object?> result = null;
  207. try
  208. {
  209. var param = Expression.Parameter(typeof(T), "o");
  210. Expression body = param;
  211. foreach (var member in propname.Split('.'))
  212. body = Expression.PropertyOrField(body, member);
  213. Expression keyExpr = Expression.Constant(key, typeof(string));
  214. var valueExpr = Expression.Parameter(typeof(object));
  215. // Alternative, note that we could even look for the type of parameters, if there are indexer overloads.
  216. var indexer = (from p in body.Type.GetDefaultMembers().OfType<PropertyInfo>()
  217. // This check is probably useless. You can't overload on return value in C#.
  218. where p.PropertyType == typeof(object)
  219. let q = p.GetIndexParameters()
  220. // 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.
  221. where q.Length == 1 && q[0].ParameterType == typeof(string)
  222. select p).Single();
  223. var indexExpr = Expression.Property(body, indexer, keyExpr);
  224. var assign = Expression.Assign(indexExpr, valueExpr);
  225. var lambdaSetter = Expression.Lambda<Action<T, object?>>(assign, param, valueExpr);
  226. result = lambdaSetter.Compile();
  227. }
  228. catch (Exception e)
  229. {
  230. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  231. }
  232. return result;
  233. }
  234. public static Action<object, object?> Setter(Type objectType, string propname, string key)
  235. {
  236. Action<object, object?> result = null;
  237. try
  238. {
  239. var objectParameter = Expression.Parameter(typeof(object), "o");
  240. var param = Expression.ConvertChecked(objectParameter, objectType);
  241. Expression body = param;
  242. foreach (var member in propname.Split('.'))
  243. body = Expression.PropertyOrField(body, member);
  244. Expression keyExpr = Expression.Constant(key, typeof(string));
  245. var valueExpr = Expression.Parameter(typeof(object));
  246. // Alternative, note that we could even look for the type of parameters, if there are indexer overloads.
  247. var indexer = (from p in body.Type.GetDefaultMembers().OfType<PropertyInfo>()
  248. // This check is probably useless. You can't overload on return value in C#.
  249. where p.PropertyType == typeof(object)
  250. let q = p.GetIndexParameters()
  251. // 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.
  252. where q.Length == 1 && q[0].ParameterType == typeof(string)
  253. select p).Single();
  254. var indexExpr = Expression.Property(body, indexer, keyExpr);
  255. var assign = Expression.Assign(indexExpr, valueExpr);
  256. var lambdaSetter = Expression.Lambda<Action<object, object?>>(assign, objectParameter, valueExpr);
  257. result = lambdaSetter.Compile();
  258. }
  259. catch (Exception e)
  260. {
  261. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  262. }
  263. return result;
  264. }
  265. #endregion
  266. }
  267. }