Expressions.cs 14 KB

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