Expression.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. // Permission is hereby granted, free of charge, to any person obtaining
  2. // a copy of this software and associated documentation files (the
  3. // "Software"), to deal in the Software without restriction, including
  4. // without limitation the rights to use, copy, modify, merge, publish,
  5. // distribute, sublicense, and/or sell copies of the Software, and to
  6. // permit persons to whom the Software is furnished to do so, subject to
  7. // the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be
  10. // included in all copies or substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  13. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  14. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  15. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  16. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  17. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  18. //
  19. // Authors:
  20. // Marek Safar ([email protected])
  21. // Antonello Provenzano <[email protected]>
  22. // Federico Di Gregorio <[email protected]>
  23. using System.Collections.Generic;
  24. using System.Collections.ObjectModel;
  25. using System.Reflection;
  26. using System.Text;
  27. namespace System.Linq.Expressions
  28. {
  29. public abstract class Expression
  30. {
  31. #region .ctor
  32. protected Expression (ExpressionType nodeType, Type type)
  33. {
  34. this.nodeType = nodeType;
  35. this.type = type;
  36. }
  37. #endregion
  38. #region Fields
  39. private Type type;
  40. private ExpressionType nodeType;
  41. #endregion
  42. #region Properties
  43. public Type Type {
  44. get { return type; }
  45. }
  46. public ExpressionType NodeType {
  47. get { return nodeType; }
  48. }
  49. #endregion
  50. #region Internal methods
  51. internal virtual void BuildString (StringBuilder builder)
  52. {
  53. builder.Append ("[").Append (nodeType).Append ("]");
  54. }
  55. internal static Type GetNonNullableType(Type type)
  56. {
  57. // The Nullable<> class takes a single generic type so we can directly return
  58. // the first element of the array (if the type is nullable.)
  59. if (IsNullableType (type))
  60. return type.GetGenericArguments ()[0];
  61. else
  62. return type;
  63. }
  64. internal static bool IsNullableType(Type type)
  65. {
  66. if (type == null)
  67. throw new ArgumentNullException("type");
  68. if (type.IsGenericType) {
  69. Type genType = type.GetGenericTypeDefinition();
  70. return typeof(Nullable<>).IsAssignableFrom(genType);
  71. }
  72. return false;
  73. }
  74. #endregion
  75. #region Private support methods
  76. private const BindingFlags opBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
  77. private const BindingFlags methBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
  78. private const BindingFlags propBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
  79. private static MethodInfo GetUserDefinedBinaryOperator (Type leftType, Type rightType, string name)
  80. {
  81. Type[] types = new Type[2] { leftType, rightType };
  82. MethodInfo method = leftType.GetMethod (name, opBindingFlags, null, types, null);
  83. if (method != null) return method;
  84. method = rightType.GetMethod (name, opBindingFlags, null, types, null);
  85. if (method != null) return method;
  86. if (method == null && IsNullableType(leftType) && IsNullableType(rightType))
  87. return GetUserDefinedBinaryOperator(GetNonNullableType(leftType), GetNonNullableType(rightType), name);
  88. return null;
  89. }
  90. private static BinaryExpression GetUserDefinedBinaryOperatorOrThrow (ExpressionType nodeType, string name,
  91. Expression left, Expression right)
  92. {
  93. MethodInfo method = GetUserDefinedBinaryOperator(left.type, right.type, name);
  94. if (method != null)
  95. return new BinaryExpression (nodeType, left, right, method, method.ReturnType);
  96. else
  97. throw new InvalidOperationException(String.Format(
  98. "The binary operator Add is not defined for the types '{0}' and '{1}'.", left.type, right.type));
  99. // Note: here the code in ExpressionUtils has a series of checks to make sure that
  100. // the method is static, that its return type is not void and that the number of
  101. // parameters is 2 and they are of the right type, but we already know that! Or not?
  102. }
  103. private static PropertyInfo GetProperty (MethodInfo mi)
  104. {
  105. // If the method has the hidebysig and specialname attributes it can be a property accessor;
  106. // if that's the case we try to extract the type of the property and then we use it and the
  107. // property name (derived from the method name) to find the right ProprtyInfo.
  108. if (mi.IsHideBySig && mi.IsSpecialName) {
  109. Type propertyType = null;
  110. if (mi.Name.StartsWith("set_")) {
  111. ParameterInfo[] parameters = mi.GetParameters();
  112. if (parameters.Length == 1)
  113. propertyType = parameters[0].ParameterType;
  114. }
  115. else if (mi.Name.StartsWith("get_")) {
  116. propertyType = mi.ReturnType;
  117. }
  118. if (propertyType != null) {
  119. PropertyInfo pi = mi.DeclaringType.GetProperty(
  120. mi.Name.Substring(4), propBindingFlags, null, propertyType, new Type[0], null);
  121. if (pi != null) return pi;
  122. }
  123. }
  124. throw new ArgumentException(String.Format(
  125. "The method '{0}.{1}' is not a property accessor", mi.DeclaringType.FullName, mi.Name));
  126. }
  127. private static void ValidateUserDefinedConditionalLogicOperator (ExpressionType nodeType, Type left, Type right, MethodInfo method)
  128. {
  129. // Conditional logic need the "definitely true" and "definitely false" operators.
  130. Type[] types = new Type[1] { left };
  131. MethodInfo opTrue = left.GetMethod ("op_True", opBindingFlags, null, types, null);
  132. MethodInfo opFalse = left.GetMethod ("op_False", opBindingFlags, null, types, null);
  133. if (opTrue == null || opFalse == null)
  134. throw new ArgumentException(String.Format(
  135. "The user-defined operator method '{0}' for operator '{1}' must have associated boolean True and False operators.",
  136. method.Name, nodeType));
  137. }
  138. private static void ValidateSettableFieldOrPropertyMember (MemberInfo member, out Type memberType)
  139. {
  140. if (member.MemberType == MemberTypes.Field) {
  141. memberType = typeof (FieldInfo);
  142. }
  143. else if (member.MemberType == MemberTypes.Property) {
  144. PropertyInfo pi = (PropertyInfo)member;
  145. if (!pi.CanWrite)
  146. throw new ArgumentException(String.Format("The property '{0}' has no 'set' accessor", pi));
  147. memberType = typeof(PropertyInfo);
  148. }
  149. else {
  150. throw new ArgumentException("Argument must be either a FieldInfo or PropertyInfo");
  151. }
  152. }
  153. #endregion
  154. #region ToString
  155. public override string ToString()
  156. {
  157. StringBuilder builder = new StringBuilder ();
  158. BuildString (builder);
  159. return builder.ToString ();
  160. }
  161. #endregion
  162. #region Add
  163. public static BinaryExpression Add(Expression left, Expression right, MethodInfo method)
  164. {
  165. if (left == null)
  166. throw new ArgumentNullException ("left");
  167. if (right == null)
  168. throw new ArgumentNullException ("right");
  169. if (method != null)
  170. return new BinaryExpression(ExpressionType.Add, left, right, method, method.ReturnType);
  171. // Since both the expressions define the same numeric type we don't have
  172. // to look for the "op_Addition" method.
  173. if (left.type == right.type && ExpressionUtil.IsNumber(left.type))
  174. return new BinaryExpression(ExpressionType.Add, left, right, left.type);
  175. // Else we try for a user-defined operator.
  176. return GetUserDefinedBinaryOperatorOrThrow (ExpressionType.Add, "op_Addition", left, right);
  177. }
  178. public static BinaryExpression Add(Expression left, Expression right)
  179. {
  180. return Add(left, right, null);
  181. }
  182. #endregion
  183. #region AddChecked
  184. public static BinaryExpression AddChecked(Expression left, Expression right, MethodInfo method)
  185. {
  186. if (left == null)
  187. throw new ArgumentNullException ("left");
  188. if (right == null)
  189. throw new ArgumentNullException ("right");
  190. if (method != null)
  191. return new BinaryExpression(ExpressionType.AddChecked, left, right, method, method.ReturnType);
  192. // Since both the expressions define the same numeric type we don't have
  193. // to look for the "op_Addition" method.
  194. if (left.type == right.type && ExpressionUtil.IsNumber(left.type))
  195. return new BinaryExpression(ExpressionType.AddChecked, left, right, left.type);
  196. method = GetUserDefinedBinaryOperator (left.type, right.type, "op_Addition");
  197. if (method == null)
  198. throw new InvalidOperationException(String.Format(
  199. "The binary operator AddChecked is not defined for the types '{0}' and '{1}'.", left.type, right.type));
  200. Type retType = method.ReturnType;
  201. // Note: here the code did some very strange checks for bool (but note that bool does
  202. // not define an addition operator) and created nullables for value types (but the new
  203. // MS code does not do that). All that has been removed.
  204. return new BinaryExpression(ExpressionType.AddChecked, left, right, method, retType);
  205. }
  206. public static BinaryExpression AddChecked(Expression left, Expression right)
  207. {
  208. return AddChecked(left, right, null);
  209. }
  210. #endregion
  211. #region And
  212. public static BinaryExpression And(Expression left, Expression right, MethodInfo method)
  213. {
  214. if (left == null)
  215. throw new ArgumentNullException ("left");
  216. if (right == null)
  217. throw new ArgumentNullException ("right");
  218. if (method != null)
  219. return new BinaryExpression(ExpressionType.And, left, right, method, method.ReturnType);
  220. // Since both the expressions define the same integer or boolean type we don't have
  221. // to look for the "op_BitwiseAnd" method.
  222. if (left.type == right.type && (ExpressionUtil.IsInteger(left.type) || left.type == typeof(bool)))
  223. return new BinaryExpression(ExpressionType.And, left, right, left.type);
  224. // Else we try for a user-defined operator.
  225. return GetUserDefinedBinaryOperatorOrThrow (ExpressionType.And, "op_BitwiseAnd", left, right);
  226. }
  227. public static BinaryExpression And(Expression left, Expression right)
  228. {
  229. return And(left, right, null);
  230. }
  231. #endregion
  232. #region AndAlso
  233. public static BinaryExpression AndAlso(Expression left, Expression right, MethodInfo method)
  234. {
  235. if (left == null)
  236. throw new ArgumentNullException ("left");
  237. if (right == null)
  238. throw new ArgumentNullException ("right");
  239. // Since both the expressions define the same integer or boolean type we don't have
  240. // to look for the "op_BitwiseAnd" method.
  241. if (left.type == right.type && left.type == typeof(bool))
  242. return new BinaryExpression(ExpressionType.AndAlso, left, right, left.type);
  243. // Else we must validate the method to make sure it has companion "true" and "false" operators.
  244. if (method == null)
  245. method = GetUserDefinedBinaryOperator (left.type, right.type, "op_BitwiseAnd");
  246. if (method == null)
  247. throw new InvalidOperationException(String.Format(
  248. "The binary operator AndAlso is not defined for the types '{0}' and '{1}'.", left.type, right.type));
  249. ValidateUserDefinedConditionalLogicOperator(ExpressionType.AndAlso, left.type, right.type, method);
  250. return new BinaryExpression(ExpressionType.AndAlso, left, right, method, method.ReturnType);
  251. }
  252. public static BinaryExpression AndAlso(Expression left, Expression right)
  253. {
  254. return AndAlso(left, right, null);
  255. }
  256. #endregion
  257. #region ArrayIndex
  258. public static BinaryExpression ArrayIndex(Expression array, Expression index)
  259. {
  260. if (array == null)
  261. throw new ArgumentNullException ("array");
  262. if (index == null)
  263. throw new ArgumentNullException ("index");
  264. if (!array.type.IsArray)
  265. throw new ArgumentException ("Argument must be array");
  266. if (index.type != typeof(int))
  267. throw new ArgumentException ("Argument for array index must be of type Int32");
  268. return new BinaryExpression(ExpressionType.ArrayIndex, array, index, array.type.GetElementType());
  269. }
  270. public static MethodCallExpression ArrayIndex(Expression array, params Expression[] indexes)
  271. {
  272. return ArrayIndex(array, (IEnumerable<Expression>)indexes);
  273. }
  274. public static MethodCallExpression ArrayIndex(Expression array, IEnumerable<Expression> indexes)
  275. {
  276. if (array == null)
  277. throw new ArgumentNullException ("array");
  278. if (indexes == null)
  279. throw new ArgumentNullException ("indexes");
  280. if (!array.type.IsArray)
  281. throw new ArgumentException ("Argument must be array");
  282. // We'll need an array of typeof(Type) elements long as the array's rank later
  283. // and also a generic List to hold the indexes (ReadOnlyCollection wants that.)
  284. Type[] types = (Type[])Array.CreateInstance(typeof(Type), array.type.GetArrayRank());
  285. Expression[] indexesList = new Expression[array.type.GetArrayRank()];
  286. int rank = 0;
  287. foreach (Expression index in indexes) {
  288. if (index.type != typeof(int))
  289. throw new ArgumentException ("Argument for array index must be of type Int32");
  290. if (rank == array.type.GetArrayRank())
  291. throw new ArgumentException ("Incorrect number of indexes");
  292. types[rank] = index.type;
  293. indexesList[rank] = index;
  294. rank += 1;
  295. }
  296. // If the array's rank is equalto the number of given indexes we can go on and
  297. // look for a Get(Int32, ...) method with "rank" parameters to generate the
  298. // MethodCallExpression.
  299. MethodInfo method = array.type.GetMethod("Get", methBindingFlags, null, types, null);
  300. // This should not happen, but we check anyway.
  301. if (method == null)
  302. throw new InvalidOperationException(String.Format(
  303. "The method Get(...) is not defined for the type '{0}'.", array.type));
  304. return new MethodCallExpression(ExpressionType.Call, method, array, new ReadOnlyCollection<Expression>(indexesList));
  305. }
  306. #endregion
  307. #region ArrayLength
  308. public static UnaryExpression ArrayLength(Expression array)
  309. {
  310. if (array == null)
  311. throw new ArgumentNullException ("array");
  312. if (!array.type.IsArray)
  313. throw new ArgumentException ("Argument must be array");
  314. return new UnaryExpression(ExpressionType.ArrayLength, array, typeof(Int32));
  315. }
  316. #endregion
  317. #region Bind
  318. public static MemberAssignment Bind (MemberInfo member, Expression expression)
  319. {
  320. if (member == null)
  321. throw new ArgumentNullException ("member");
  322. if (expression == null)
  323. throw new ArgumentNullException ("expression");
  324. Type memberType;
  325. ValidateSettableFieldOrPropertyMember(member, out memberType);
  326. return new MemberAssignment(member, expression);
  327. }
  328. public static MemberAssignment Bind (MethodInfo propertyAccessor, Expression expression)
  329. {
  330. if (propertyAccessor == null)
  331. throw new ArgumentNullException ("propertyAccessor");
  332. if (expression == null)
  333. throw new ArgumentNullException ("expression");
  334. return new MemberAssignment(GetProperty(propertyAccessor), expression);
  335. }
  336. #endregion
  337. #region Call
  338. public static MethodCallExpression Call(Expression instance, MethodInfo method)
  339. {
  340. if (method == null)
  341. throw new ArgumentNullException("method");
  342. if (instance == null && !method.IsStatic)
  343. throw new ArgumentNullException("instance");
  344. return Call(instance, method, (Expression[])null);
  345. }
  346. public static MethodCallExpression Call(MethodInfo method, params Expression[] arguments)
  347. {
  348. return Call(null, method, (IEnumerable<Expression>)arguments);
  349. }
  350. public static MethodCallExpression Call(Expression instance, MethodInfo method, params Expression[] arguments)
  351. {
  352. return Call(instance, method, (IEnumerable<Expression>)arguments);
  353. }
  354. public static MethodCallExpression Call(Expression instance, MethodInfo method, IEnumerable<Expression> arguments)
  355. {
  356. if (method == null)
  357. throw new ArgumentNullException("method");
  358. if (arguments == null)
  359. throw new ArgumentNullException("arguments");
  360. if (instance == null && !method.IsStatic)
  361. throw new ArgumentNullException("instance");
  362. if (method.IsGenericMethodDefinition)
  363. throw new ArgumentException();
  364. if (method.ContainsGenericParameters)
  365. throw new ArgumentException();
  366. if (instance != null && !instance.type.IsAssignableFrom(method.DeclaringType))
  367. throw new ArgumentException();
  368. ReadOnlyCollection<Expression> roArgs = Enumerable.ToReadOnlyCollection<Expression>(arguments);
  369. ParameterInfo[] pars = method.GetParameters();
  370. if (Enumerable.Count<Expression>(arguments) != pars.Length)
  371. throw new ArgumentException();
  372. if (pars.Length > 0)
  373. {
  374. //TODO: validate the parameters against the arguments...
  375. }
  376. return new MethodCallExpression(ExpressionType.Call, method, instance, roArgs);
  377. }
  378. #endregion
  379. // NOTE: CallVirtual is not implemented because it is already marked as Obsolete by MS.
  380. public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse)
  381. {
  382. if (test == null)
  383. throw new ArgumentNullException("test");
  384. if (ifTrue == null)
  385. throw new ArgumentNullException("ifTrue");
  386. if (ifFalse == null)
  387. throw new ArgumentNullException("ifFalse");
  388. if (test.type != typeof(bool))
  389. throw new ArgumentException();
  390. if (ifTrue.type != ifFalse.type)
  391. throw new ArgumentException();
  392. return new ConditionalExpression(test, ifTrue, ifFalse, ifTrue.type);
  393. }
  394. public static ConstantExpression Constant(object value, Type type)
  395. {
  396. if (type == null)
  397. throw new ArgumentNullException("type");
  398. if (value == null && !IsNullableType(type))
  399. throw new ArgumentException("Argument types do not match");
  400. return new ConstantExpression(value, type);
  401. }
  402. public static ConstantExpression Constant(object value)
  403. {
  404. if (value != null)
  405. return new ConstantExpression(value, value.GetType());
  406. else
  407. return new ConstantExpression(null, typeof(object));
  408. }
  409. public static BinaryExpression Divide(Expression left, Expression right)
  410. {
  411. return Divide(left, right, null);
  412. }
  413. public static BinaryExpression Divide(Expression left, Expression right, MethodInfo method)
  414. {
  415. if (left == null)
  416. throw new ArgumentNullException("left");
  417. if (right == null)
  418. throw new ArgumentNullException("right");
  419. // sine both the expressions define the same numeric type we don't have
  420. // to look for the "op_Division" method...
  421. if (left.type == right.type &&
  422. ExpressionUtil.IsNumber(left.type))
  423. return new BinaryExpression(ExpressionType.Divide, left, right, left.type);
  424. if (method == null)
  425. method = ExpressionUtil.GetOperatorMethod("op_Division", left.type, right.type);
  426. // ok if even op_Division is not defined we need to throw an exception...
  427. if (method == null)
  428. throw new InvalidOperationException();
  429. return new BinaryExpression(ExpressionType.Divide, left, right, method, method.ReturnType);
  430. }
  431. public static MemberExpression Field(Expression expression, FieldInfo field)
  432. {
  433. if (field == null)
  434. throw new ArgumentNullException("field");
  435. return new MemberExpression(expression, field, field.FieldType);
  436. }
  437. public static MemberExpression Field(Expression expression, string fieldName)
  438. {
  439. if (expression == null)
  440. throw new ArgumentNullException("expression");
  441. FieldInfo field = expression.Type.GetField(fieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
  442. if (field == null)
  443. throw new ArgumentException();
  444. return Field(expression, field);
  445. }
  446. public static FuncletExpression Funclet(Funclet funclet, Type type)
  447. {
  448. if (funclet == null)
  449. throw new ArgumentNullException("funclet");
  450. if (type == null)
  451. throw new ArgumentNullException("type");
  452. return new FuncletExpression(funclet, type);
  453. }
  454. public static Type GetFuncType(params Type[] typeArgs)
  455. {
  456. if (typeArgs == null)
  457. throw new ArgumentNullException("typeArgs");
  458. if (typeArgs.Length > 5)
  459. throw new ArgumentException();
  460. return typeof(Func<,,,,>).MakeGenericType(typeArgs);
  461. }
  462. public static BinaryExpression LeftShift(Expression left, Expression right, MethodInfo method)
  463. {
  464. if (left == null)
  465. throw new ArgumentNullException("left");
  466. if (right == null)
  467. throw new ArgumentNullException("right");
  468. // since the left expression is of an integer type and the right is of
  469. // an integer we don't have to look for the "op_LeftShift" method...
  470. if (ExpressionUtil.IsInteger(left.type) && right.type == typeof(int))
  471. return new BinaryExpression(ExpressionType.LeftShift, left, right, left.type);
  472. if (method == null)
  473. method = ExpressionUtil.GetOperatorMethod("op_LeftShift", left.type, right.type);
  474. // ok if even op_Division is not defined we need to throw an exception...
  475. if (method == null)
  476. throw new InvalidOperationException();
  477. return new BinaryExpression(ExpressionType.LeftShift, left, right, method, method.ReturnType);
  478. }
  479. public static BinaryExpression LeftShift(Expression left, Expression right)
  480. {
  481. return LeftShift(left, right, null);
  482. }
  483. public static ListInitExpression ListInit(NewExpression newExpression, params Expression[] initializers)
  484. {
  485. if (initializers == null)
  486. throw new ArgumentNullException("inizializers");
  487. return ListInit(newExpression, Enumerable.ToReadOnlyCollection<Expression>(initializers));
  488. }
  489. public static ListInitExpression ListInit(NewExpression newExpression, IEnumerable<Expression> initializers)
  490. {
  491. if (newExpression == null)
  492. throw new ArgumentNullException("newExpression");
  493. if (initializers == null)
  494. throw new ArgumentNullException("inizializers");
  495. return new ListInitExpression(newExpression, Enumerable.ToReadOnlyCollection<Expression>(initializers));
  496. }
  497. public static MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable<MemberBinding> bindings)
  498. {
  499. if (newExpression == null)
  500. throw new ArgumentNullException("newExpression");
  501. if (bindings == null)
  502. throw new ArgumentNullException("bindings");
  503. return new MemberInitExpression(newExpression, Enumerable.ToReadOnlyCollection<MemberBinding>(bindings));
  504. }
  505. public static MemberExpression Property(Expression expression, PropertyInfo property)
  506. {
  507. if (property == null)
  508. throw new ArgumentNullException("property");
  509. MethodInfo getMethod = property.GetGetMethod(true);
  510. if (getMethod == null)
  511. throw new ArgumentException(); // to access the property we need to have
  512. // a get method...
  513. return new MemberExpression(expression, property, property.PropertyType);
  514. }
  515. public static UnaryExpression Quote(Expression expression)
  516. {
  517. if (expression == null)
  518. throw new ArgumentNullException("expression");
  519. return new UnaryExpression(ExpressionType.Quote, expression, expression.GetType());
  520. }
  521. public static MemberExpression Property(Expression expression, string propertyName)
  522. {
  523. if (expression == null)
  524. throw new ArgumentNullException("expression");
  525. PropertyInfo property = expression.Type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  526. if (property == null)
  527. throw new ArgumentException();
  528. return Property(expression, property);
  529. }
  530. public static MemberExpression PropertyOrField(Expression expression, string propertyOrFieldName)
  531. {
  532. if (expression == null)
  533. throw new ArgumentNullException("expression");
  534. PropertyInfo property = expression.Type.GetProperty(propertyOrFieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
  535. if (property != null)
  536. return Property(expression, property);
  537. FieldInfo field = expression.Type.GetField(propertyOrFieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
  538. if (field != null)
  539. return Field(expression, field);
  540. //TODO: should we return <null> here?
  541. // the name is not defined in the Type of the expression given...
  542. throw new ArgumentException();
  543. }
  544. public static TypeBinaryExpression TypeIs(Expression expression, Type type)
  545. {
  546. if (expression == null)
  547. throw new ArgumentNullException("expression");
  548. if (type == null)
  549. throw new ArgumentNullException("type");
  550. return new TypeBinaryExpression(ExpressionType.TypeIs, expression, type, typeof(bool));
  551. }
  552. public static UnaryExpression TypeAs(Expression expression, Type type)
  553. {
  554. if (expression == null)
  555. throw new ArgumentNullException("expression");
  556. if (type == null)
  557. throw new ArgumentNullException("type");
  558. return new UnaryExpression(ExpressionType.TypeAs, expression, type);
  559. }
  560. }
  561. }