Expression.cs 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198
  1. //
  2. // Expression.cs
  3. //
  4. // Author:
  5. // Marek Safar ([email protected])
  6. // Antonello Provenzano <[email protected]>
  7. // Federico Di Gregorio <[email protected]>
  8. // Jb Evain ([email protected])
  9. //
  10. // (C) 2008 Novell, Inc. (http://www.novell.com)
  11. //
  12. // Permission is hereby granted, free of charge, to any person obtaining
  13. // a copy of this software and associated documentation files (the
  14. // "Software"), to deal in the Software without restriction, including
  15. // without limitation the rights to use, copy, modify, merge, publish,
  16. // distribute, sublicense, and/or sell copies of the Software, and to
  17. // permit persons to whom the Software is furnished to do so, subject to
  18. // the following conditions:
  19. //
  20. // The above copyright notice and this permission notice shall be
  21. // included in all copies or substantial portions of the Software.
  22. //
  23. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  24. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  26. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  27. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  28. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  29. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  30. //
  31. using System;
  32. using System.Collections.Generic;
  33. using System.Collections.ObjectModel;
  34. using System.Reflection;
  35. using System.Text;
  36. namespace System.Linq.Expressions
  37. {
  38. public abstract class Expression
  39. {
  40. Type type;
  41. ExpressionType nodeType;
  42. public Type Type {
  43. get { return type; }
  44. }
  45. public ExpressionType NodeType {
  46. get { return nodeType; }
  47. }
  48. protected Expression (ExpressionType nodeType, Type type)
  49. {
  50. this.nodeType = nodeType;
  51. this.type = type;
  52. }
  53. #region Internal methods
  54. internal virtual void BuildString (StringBuilder builder)
  55. {
  56. builder.Append ("[").Append (nodeType).Append ("]");
  57. }
  58. internal static Type GetNonNullableType(Type type)
  59. {
  60. // The Nullable<> class takes a single generic type so we can directly return
  61. // the first element of the array (if the type is nullable.)
  62. if (IsNullableType (type))
  63. return type.GetGenericArguments ()[0];
  64. else
  65. return type;
  66. }
  67. internal static bool IsNullableType (Type type)
  68. {
  69. if (type == null)
  70. throw new ArgumentNullException ("type");
  71. return type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>);
  72. }
  73. #endregion
  74. #region Private support methods
  75. private static int IsWhat(Type type)
  76. {
  77. // This method return a "type code" that can be easily compared to a bitmask
  78. // to determine the "broad type" (integer, boolean, floating-point) of the given type.
  79. // It is used by the three methods below.
  80. if (IsNullableType (type))
  81. type = GetNonNullableType (type);
  82. switch (Type.GetTypeCode (type)) {
  83. case TypeCode.Byte: case TypeCode.SByte:
  84. case TypeCode.Int16: case TypeCode.UInt16:
  85. case TypeCode.Int32: case TypeCode.UInt32:
  86. case TypeCode.Int64: case TypeCode.UInt64:
  87. return 1;
  88. case TypeCode.Boolean:
  89. return 2;
  90. case TypeCode.Single:
  91. case TypeCode.Double:
  92. case TypeCode.Decimal:
  93. return 4;
  94. default:
  95. return 0;
  96. }
  97. }
  98. private static bool IsInteger (Type type)
  99. {
  100. return (IsWhat(type) & 1) != 0;
  101. }
  102. private static bool IsIntegerOrBool (Type type)
  103. {
  104. return (IsWhat(type) & 3) != 0;
  105. }
  106. private static bool IsNumeric (Type type)
  107. {
  108. return (IsWhat(type) & 5) != 0;
  109. }
  110. private const BindingFlags opBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
  111. private static MethodInfo GetUserDefinedBinaryOperator (Type leftType, Type rightType, string name)
  112. {
  113. Type[] types = new Type[2] { leftType, rightType };
  114. MethodInfo method;
  115. method = leftType.GetMethod (name, opBindingFlags, null, types, null);
  116. if (method != null) return method;
  117. method = rightType.GetMethod (name, opBindingFlags, null, types, null);
  118. if (method != null) return method;
  119. if (method == null && IsNullableType (leftType) && IsNullableType (rightType))
  120. return GetUserDefinedBinaryOperator (GetNonNullableType (leftType), GetNonNullableType (rightType), name);
  121. return null;
  122. }
  123. private static BinaryExpression GetUserDefinedBinaryOperatorOrThrow (ExpressionType nodeType, string name,
  124. Expression left, Expression right)
  125. {
  126. MethodInfo method = GetUserDefinedBinaryOperator(left.type, right.type, name);
  127. if (method != null)
  128. return new BinaryExpression (nodeType, left, right, method, method.ReturnType);
  129. else
  130. throw new InvalidOperationException (String.Format (
  131. "The binary operator Add is not defined for the types '{0}' and '{1}'.", left.type, right.type));
  132. // Note: here the code in ExpressionUtils has a series of checks to make sure that
  133. // the method is static, that its return type is not void and that the number of
  134. // parameters is 2 and they are of the right type, but we already know that! Or not?
  135. }
  136. private static MethodInfo FindMethod (Type type, string methodName, Type [] typeArgs, Expression [] args, BindingFlags flags)
  137. {
  138. MemberInfo[] members = type.FindMembers(MemberTypes.Method, flags,
  139. delegate(MemberInfo mi, object obj) { return mi.Name == (String)obj; },
  140. methodName);
  141. if (members.Length == 0)
  142. throw new InvalidOperationException (String.Format (
  143. "No method '{0}' exists on type '{1}'.", methodName, type.FullName));
  144. MethodInfo methodDefinition = null;
  145. MethodInfo method = null;
  146. int methodCount = 1;
  147. foreach (MemberInfo member in members) {
  148. MethodInfo mi = (MethodInfo)member;
  149. if (mi.IsGenericMethodDefinition) {
  150. // If the generic method definition matches we save it away to be able to make the
  151. // correct closed method later on.
  152. Type[] genericArgs = mi.GetGenericArguments();
  153. if (genericArgs.Length != typeArgs.Length) goto next;
  154. methodDefinition = mi;
  155. goto next;
  156. }
  157. // If there is a discrepancy between method's generic types and the given types or if
  158. // the method is open we simply discard it and go on.
  159. if ((mi.IsGenericMethod && (typeArgs == null || mi.ContainsGenericParameters))
  160. || (!mi.IsGenericMethod && typeArgs != null))
  161. goto next;
  162. // If the method is a closed generic we try to match the generic types.
  163. if (mi.IsGenericMethod) {
  164. Type[] genericArgs = mi.GetGenericArguments();
  165. if (genericArgs.Length != typeArgs.Length) goto next;
  166. for (int i=0 ; i < genericArgs.Length ; i++)
  167. if (genericArgs[i] != typeArgs[i]) goto next;
  168. }
  169. // Finally we test for the method's parameters.
  170. ParameterInfo[] parameters = mi.GetParameters ();
  171. if (parameters.Length != args.Length) goto next;
  172. for (int i=0 ; i < parameters.Length ; i++)
  173. if (parameters[i].ParameterType != args[i].type) goto next;
  174. method = mi;
  175. break;
  176. next:
  177. continue;
  178. }
  179. if (method != null)
  180. return method;
  181. else
  182. throw new InvalidOperationException(String.Format(
  183. "No method '{0}' on type '{1}' is compatible with the supplied arguments.", methodName, type.FullName));
  184. }
  185. private static PropertyInfo GetProperty (MethodInfo mi)
  186. {
  187. // If the method has the hidebysig and specialname attributes it can be a property accessor;
  188. // if that's the case we try to extract the type of the property and then we use it and the
  189. // property name (derived from the method name) to find the right PropertyInfo.
  190. if (mi.IsHideBySig && mi.IsSpecialName) {
  191. Type propertyType = null;
  192. if (mi.Name.StartsWith("set_")) {
  193. ParameterInfo[] parameters = mi.GetParameters();
  194. if (parameters.Length == 1)
  195. propertyType = parameters[0].ParameterType;
  196. }
  197. else if (mi.Name.StartsWith("get_")) {
  198. propertyType = mi.ReturnType;
  199. }
  200. if (propertyType != null) {
  201. PropertyInfo pi = mi.DeclaringType.GetProperty(mi.Name.Substring(4),
  202. BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance,
  203. null, propertyType, new Type[0], null);
  204. if (pi != null) return pi;
  205. }
  206. }
  207. throw new ArgumentException (String.Format(
  208. "The method '{0}.{1}' is not a property accessor", mi.DeclaringType.FullName, mi.Name));
  209. }
  210. private static void ValidateUserDefinedConditionalLogicOperator (ExpressionType nodeType, Type left, Type right, MethodInfo method)
  211. {
  212. // Conditional logic need the "definitely true" and "definitely false" operators.
  213. Type[] types = new Type[1] { left };
  214. MethodInfo opTrue = left.GetMethod ("op_True", opBindingFlags, null, types, null);
  215. MethodInfo opFalse = left.GetMethod ("op_False", opBindingFlags, null, types, null);
  216. if (opTrue == null || opFalse == null)
  217. throw new ArgumentException (String.Format (
  218. "The user-defined operator method '{0}' for operator '{1}' must have associated boolean True and False operators.",
  219. method.Name, nodeType));
  220. }
  221. private static void ValidateSettableFieldOrPropertyMember (MemberInfo member, out Type memberType)
  222. {
  223. if (member.MemberType == MemberTypes.Field) {
  224. memberType = (member as FieldInfo).FieldType;
  225. }
  226. else if (member.MemberType == MemberTypes.Property) {
  227. PropertyInfo pi = (PropertyInfo)member;
  228. if (!pi.CanWrite)
  229. throw new ArgumentException (String.Format ("The property '{0}' has no 'set' accessor", pi));
  230. memberType = (member as PropertyInfo).PropertyType;
  231. }
  232. else {
  233. throw new ArgumentException ("Argument must be either a FieldInfo or PropertyInfo");
  234. }
  235. }
  236. private static void ValidateGettableFieldOrPropertyMember (MemberInfo member, out Type memberType)
  237. {
  238. if (member.MemberType == MemberTypes.Field) {
  239. memberType = (member as FieldInfo).FieldType;
  240. }
  241. else if (member.MemberType == MemberTypes.Property) {
  242. PropertyInfo pi = (PropertyInfo)member;
  243. if (!pi.CanRead)
  244. throw new ArgumentException (String.Format ("The property '{0}' has no 'get' accessor", pi));
  245. memberType = (member as PropertyInfo).PropertyType;
  246. }
  247. else {
  248. throw new ArgumentException ("Argument must be either a FieldInfo or PropertyInfo");
  249. }
  250. }
  251. #endregion
  252. #region ToString
  253. public override string ToString()
  254. {
  255. StringBuilder builder = new StringBuilder ();
  256. BuildString (builder);
  257. return builder.ToString ();
  258. }
  259. #endregion
  260. #region Add
  261. public static BinaryExpression Add(Expression left, Expression right, MethodInfo method)
  262. {
  263. if (left == null)
  264. throw new ArgumentNullException ("left");
  265. if (right == null)
  266. throw new ArgumentNullException ("right");
  267. if (method != null)
  268. return new BinaryExpression(ExpressionType.Add, left, right, method, method.ReturnType);
  269. // Since both the expressions define the same numeric type we don't have
  270. // to look for the "op_Addition" method.
  271. if (left.type == right.type && IsNumeric (left.type))
  272. return new BinaryExpression(ExpressionType.Add, left, right, left.type);
  273. // Else we try for a user-defined operator.
  274. return GetUserDefinedBinaryOperatorOrThrow (ExpressionType.Add, "op_Addition", left, right);
  275. }
  276. public static BinaryExpression Add(Expression left, Expression right)
  277. {
  278. return Add(left, right, null);
  279. }
  280. #endregion
  281. #region AddChecked
  282. public static BinaryExpression AddChecked(Expression left, Expression right, MethodInfo method)
  283. {
  284. if (left == null)
  285. throw new ArgumentNullException ("left");
  286. if (right == null)
  287. throw new ArgumentNullException ("right");
  288. if (method != null)
  289. return new BinaryExpression(ExpressionType.AddChecked, left, right, method, method.ReturnType);
  290. // Since both the expressions define the same numeric type we don't have
  291. // to look for the "op_Addition" method.
  292. if (left.type == right.type && IsNumeric (left.type))
  293. return new BinaryExpression(ExpressionType.AddChecked, left, right, left.type);
  294. method = GetUserDefinedBinaryOperator (left.type, right.type, "op_Addition");
  295. if (method == null)
  296. throw new InvalidOperationException(String.Format(
  297. "The binary operator AddChecked is not defined for the types '{0}' and '{1}'.", left.type, right.type));
  298. Type retType = method.ReturnType;
  299. // Note: here the code did some very strange checks for bool (but note that bool does
  300. // not define an addition operator) and created nullables for value types (but the new
  301. // MS code does not do that). All that has been removed.
  302. return new BinaryExpression(ExpressionType.AddChecked, left, right, method, retType);
  303. }
  304. public static BinaryExpression AddChecked(Expression left, Expression right)
  305. {
  306. return AddChecked(left, right, null);
  307. }
  308. #endregion
  309. #region And
  310. public static BinaryExpression And(Expression left, Expression right, MethodInfo method)
  311. {
  312. if (left == null)
  313. throw new ArgumentNullException ("left");
  314. if (right == null)
  315. throw new ArgumentNullException ("right");
  316. if (method != null)
  317. return new BinaryExpression(ExpressionType.And, left, right, method, method.ReturnType);
  318. // Since both the expressions define the same integer or boolean type we don't have
  319. // to look for the "op_BitwiseAnd" method.
  320. if (left.type == right.type && IsIntegerOrBool (left.type))
  321. return new BinaryExpression(ExpressionType.And, left, right, left.type);
  322. // Else we try for a user-defined operator.
  323. return GetUserDefinedBinaryOperatorOrThrow (ExpressionType.And, "op_BitwiseAnd", left, right);
  324. }
  325. public static BinaryExpression And(Expression left, Expression right)
  326. {
  327. return And(left, right, null);
  328. }
  329. #endregion
  330. #region AndAlso
  331. public static BinaryExpression AndAlso(Expression left, Expression right, MethodInfo method)
  332. {
  333. if (left == null)
  334. throw new ArgumentNullException ("left");
  335. if (right == null)
  336. throw new ArgumentNullException ("right");
  337. // Since both the expressions define the same boolean type we don't have
  338. // to look for the "op_BitwiseAnd" method.
  339. if (left.type == right.type && left.type == typeof(bool))
  340. return new BinaryExpression(ExpressionType.AndAlso, left, right, left.type);
  341. // Else we must validate the method to make sure it has companion "true" and "false" operators.
  342. if (method == null)
  343. method = GetUserDefinedBinaryOperator (left.type, right.type, "op_BitwiseAnd");
  344. if (method == null)
  345. throw new InvalidOperationException(String.Format(
  346. "The binary operator AndAlso is not defined for the types '{0}' and '{1}'.", left.type, right.type));
  347. ValidateUserDefinedConditionalLogicOperator(ExpressionType.AndAlso, left.type, right.type, method);
  348. return new BinaryExpression(ExpressionType.AndAlso, left, right, method, method.ReturnType);
  349. }
  350. public static BinaryExpression AndAlso(Expression left, Expression right)
  351. {
  352. return AndAlso(left, right, null);
  353. }
  354. #endregion
  355. #region ArrayIndex
  356. public static BinaryExpression ArrayIndex(Expression array, Expression index)
  357. {
  358. if (array == null)
  359. throw new ArgumentNullException ("array");
  360. if (index == null)
  361. throw new ArgumentNullException ("index");
  362. if (!array.type.IsArray)
  363. throw new ArgumentException ("Argument must be array");
  364. if (index.type != typeof(int))
  365. throw new ArgumentException ("Argument for array index must be of type Int32");
  366. return new BinaryExpression(ExpressionType.ArrayIndex, array, index, array.type.GetElementType());
  367. }
  368. public static MethodCallExpression ArrayIndex(Expression array, params Expression[] indexes)
  369. {
  370. return ArrayIndex(array, (IEnumerable<Expression>)indexes);
  371. }
  372. public static MethodCallExpression ArrayIndex(Expression array, IEnumerable<Expression> indexes)
  373. {
  374. if (array == null)
  375. throw new ArgumentNullException ("array");
  376. if (indexes == null)
  377. throw new ArgumentNullException ("indexes");
  378. if (!array.type.IsArray)
  379. throw new ArgumentException ("Argument must be array");
  380. // We'll need an array of typeof(Type) elements long as the array's rank later
  381. // and also a generic List to hold the indexes (ReadOnlyCollection wants that.)
  382. Type[] types = (Type[])Array.CreateInstance(typeof(Type), array.type.GetArrayRank());
  383. Expression[] indexesList = new Expression[array.type.GetArrayRank()];
  384. int rank = 0;
  385. foreach (Expression index in indexes) {
  386. if (index.type != typeof(int))
  387. throw new ArgumentException ("Argument for array index must be of type Int32");
  388. if (rank == array.type.GetArrayRank())
  389. throw new ArgumentException ("Incorrect number of indexes");
  390. types[rank] = index.type;
  391. indexesList[rank] = index;
  392. rank += 1;
  393. }
  394. // If the array's rank is equalto the number of given indexes we can go on and
  395. // look for a Get(Int32, ...) method with "rank" parameters to generate the
  396. // MethodCallExpression.
  397. MethodInfo method = array.type.GetMethod("Get",
  398. BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, types, null);
  399. // This should not happen, but we check anyway.
  400. if (method == null)
  401. throw new InvalidOperationException(String.Format(
  402. "The method Get(...) is not defined for the type '{0}'.", array.type));
  403. return new MethodCallExpression(ExpressionType.Call, method, array, new ReadOnlyCollection<Expression>(indexesList));
  404. }
  405. #endregion
  406. #region ArrayLength
  407. public static UnaryExpression ArrayLength (Expression array)
  408. {
  409. if (array == null)
  410. throw new ArgumentNullException ("array");
  411. if (!array.type.IsArray)
  412. throw new ArgumentException ("Argument must be array");
  413. if (array.type.GetArrayRank () != 1)
  414. throw new ArgumentException ("Argument must be a single dimensional array");
  415. return new UnaryExpression (ExpressionType.ArrayLength, array, typeof (int));
  416. }
  417. #endregion
  418. #region Bind
  419. public static MemberAssignment Bind (MemberInfo member, Expression expression)
  420. {
  421. if (member == null)
  422. throw new ArgumentNullException ("member");
  423. if (expression == null)
  424. throw new ArgumentNullException ("expression");
  425. Type memberType;
  426. ValidateSettableFieldOrPropertyMember(member, out memberType);
  427. return new MemberAssignment(member, expression);
  428. }
  429. public static MemberAssignment Bind (MethodInfo propertyAccessor, Expression expression)
  430. {
  431. if (propertyAccessor == null)
  432. throw new ArgumentNullException ("propertyAccessor");
  433. if (expression == null)
  434. throw new ArgumentNullException ("expression");
  435. return new MemberAssignment(GetProperty(propertyAccessor), expression);
  436. }
  437. #endregion
  438. #region Call
  439. public static MethodCallExpression Call(Expression instance, MethodInfo method)
  440. {
  441. if (method == null)
  442. throw new ArgumentNullException("method");
  443. if (instance == null && !method.IsStatic)
  444. throw new ArgumentNullException("instance");
  445. return Call(instance, method, (Expression[])null);
  446. }
  447. public static MethodCallExpression Call(Expression instance, MethodInfo method, params Expression[] arguments)
  448. {
  449. return Call(instance, method, (IEnumerable<Expression>)arguments);
  450. }
  451. public static MethodCallExpression Call(Expression instance, MethodInfo method, IEnumerable<Expression> arguments)
  452. {
  453. if (method == null)
  454. throw new ArgumentNullException("method");
  455. if (arguments == null)
  456. throw new ArgumentNullException("arguments");
  457. if (instance == null && !method.IsStatic)
  458. throw new ArgumentNullException("instance");
  459. if (method.IsGenericMethodDefinition)
  460. throw new ArgumentException();
  461. if (method.ContainsGenericParameters)
  462. throw new ArgumentException();
  463. if (instance != null && !instance.type.IsAssignableFrom(method.DeclaringType))
  464. throw new ArgumentException();
  465. ReadOnlyCollection<Expression> roArgs = Enumerable.ToReadOnlyCollection<Expression>(arguments);
  466. ParameterInfo[] pars = method.GetParameters();
  467. if (Enumerable.Count<Expression>(arguments) != pars.Length)
  468. throw new ArgumentException();
  469. if (pars.Length > 0)
  470. {
  471. //TODO: validate the parameters against the arguments...
  472. }
  473. return new MethodCallExpression(ExpressionType.Call, method, instance, roArgs);
  474. }
  475. public static MethodCallExpression Call (Expression instance, string methodName, Type [] typeArguments, params Expression [] arguments)
  476. {
  477. if (instance == null)
  478. throw new ArgumentNullException("instance");
  479. if (arguments == null)
  480. throw new ArgumentNullException("arguments");
  481. return Call (null, FindMethod (instance.type, methodName, typeArguments, arguments,
  482. BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance),
  483. (IEnumerable<Expression>)arguments);
  484. }
  485. public static MethodCallExpression Call(MethodInfo method, params Expression[] arguments)
  486. {
  487. return Call(null, method, (IEnumerable<Expression>)arguments);
  488. }
  489. public static MethodCallExpression Call (Type type, string methodName, Type [] typeArguments, params Expression [] arguments)
  490. {
  491. if (type == null)
  492. throw new ArgumentNullException ("type");
  493. if (methodName == null)
  494. throw new ArgumentNullException ("methodName");
  495. if (arguments == null)
  496. throw new ArgumentNullException ("arguments");
  497. // Note that we're looking for static methods only (this version of Call() doesn't take an instance).
  498. return Call (null, FindMethod (type, methodName, typeArguments, arguments,
  499. BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static),
  500. (IEnumerable<Expression>)arguments);
  501. }
  502. #endregion
  503. // NOTE: CallVirtual is not implemented because it is already marked as Obsolete by MS.
  504. public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse)
  505. {
  506. if (test == null)
  507. throw new ArgumentNullException("test");
  508. if (ifTrue == null)
  509. throw new ArgumentNullException("ifTrue");
  510. if (ifFalse == null)
  511. throw new ArgumentNullException("ifFalse");
  512. if (test.type != typeof(bool))
  513. throw new ArgumentException();
  514. if (ifTrue.type != ifFalse.type)
  515. throw new ArgumentException();
  516. return new ConditionalExpression(test, ifTrue, ifFalse, ifTrue.type);
  517. }
  518. public static ConstantExpression Constant(object value, Type type)
  519. {
  520. if (type == null)
  521. throw new ArgumentNullException("type");
  522. if (value == null && !IsNullableType(type))
  523. throw new ArgumentException("Argument types do not match");
  524. return new ConstantExpression(value, type);
  525. }
  526. public static ConstantExpression Constant(object value)
  527. {
  528. if (value != null)
  529. return new ConstantExpression(value, value.GetType());
  530. else
  531. return new ConstantExpression(null, typeof(object));
  532. }
  533. #region Divide
  534. public static BinaryExpression Divide(Expression left, Expression right, MethodInfo method)
  535. {
  536. if (left == null)
  537. throw new ArgumentNullException ("left");
  538. if (right == null)
  539. throw new ArgumentNullException ("right");
  540. if (method != null)
  541. return new BinaryExpression(ExpressionType.Divide, left, right, method, method.ReturnType);
  542. // Since both the expressions define the same numeric type we don't have
  543. // to look for the "op_Addition" method.
  544. if (left.type == right.type && IsNumeric (left.type))
  545. return new BinaryExpression(ExpressionType.Divide, left, right, left.type);
  546. // Else we try for a user-defined operator.
  547. return GetUserDefinedBinaryOperatorOrThrow (ExpressionType.Divide, "op_Division", left, right);
  548. }
  549. public static BinaryExpression Divide(Expression left, Expression right)
  550. {
  551. return Divide(left, right, null);
  552. }
  553. #endregion
  554. #region ExclusiveOr
  555. public static BinaryExpression ExclusiveOr (Expression left, Expression right, System.Reflection.MethodInfo method)
  556. {
  557. if (left == null)
  558. throw new ArgumentNullException ("left");
  559. if (right == null)
  560. throw new ArgumentNullException ("right");
  561. if (method != null)
  562. return new BinaryExpression(ExpressionType.ExclusiveOr, left, right, method, method.ReturnType);
  563. // Since both the expressions define the same integer or boolean type we don't have
  564. // to look for the "op_BitwiseAnd" method.
  565. if (left.type == right.type && IsIntegerOrBool (left.type))
  566. return new BinaryExpression(ExpressionType.ExclusiveOr, left, right, left.type);
  567. // Else we try for a user-defined operator.
  568. return GetUserDefinedBinaryOperatorOrThrow (ExpressionType.ExclusiveOr, "op_ExclusiveOr", left, right);
  569. }
  570. public static BinaryExpression ExclusiveOr (Expression left, Expression right)
  571. {
  572. return ExclusiveOr (left, right, null);
  573. }
  574. #endregion
  575. #region Field
  576. public static MemberExpression Field (Expression expression, FieldInfo field)
  577. {
  578. // Note that expression can be (and should be) null when the access is to a static field.
  579. if (field == null)
  580. throw new ArgumentNullException("field");
  581. Type fieldType;
  582. ValidateGettableFieldOrPropertyMember(field, out fieldType);
  583. return new MemberExpression(expression, field, fieldType);
  584. }
  585. public static MemberExpression Field (Expression expression, string fieldName)
  586. {
  587. if (expression == null)
  588. throw new ArgumentNullException("expression");
  589. if (fieldName == null)
  590. throw new ArgumentNullException("fieldName");
  591. FieldInfo field = expression.Type.GetField(fieldName,
  592. BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
  593. if (field == null)
  594. throw new ArgumentException (String.Format ("Field {0} is not defined for type {1}",
  595. fieldName, expression.type.FullName));
  596. return Field(expression, field);
  597. }
  598. #endregion
  599. public static Type GetActionType (params Type [] typeArgs)
  600. {
  601. if (typeArgs == null)
  602. throw new ArgumentNullException ("typeArgs");
  603. if (typeArgs.Length > 4)
  604. throw new ArgumentException ("No Action type of this arity");
  605. if (typeArgs.Length == 0)
  606. return typeof (Action);
  607. Type action = null;
  608. switch (typeArgs.Length) {
  609. case 1:
  610. action = typeof (Action<>);
  611. break;
  612. case 2:
  613. action = typeof (Action<,>);
  614. break;
  615. case 3:
  616. action = typeof (Action<,,>);
  617. break;
  618. case 4:
  619. action = typeof (Action<,,,>);
  620. break;
  621. }
  622. return action.MakeGenericType (typeArgs);
  623. }
  624. public static Type GetFuncType (params Type [] typeArgs)
  625. {
  626. if (typeArgs == null)
  627. throw new ArgumentNullException ("typeArgs");
  628. if (typeArgs.Length < 1 || typeArgs.Length > 5)
  629. throw new ArgumentException ("No Func type of this arity");
  630. Type func = null;
  631. switch (typeArgs.Length) {
  632. case 1:
  633. func = typeof (Func<>);
  634. break;
  635. case 2:
  636. func = typeof (Func<,>);
  637. break;
  638. case 3:
  639. func = typeof (Func<,,>);
  640. break;
  641. case 4:
  642. func = typeof (Func<,,,>);
  643. break;
  644. case 5:
  645. func = typeof (Func<,,,,>);
  646. break;
  647. }
  648. return func.MakeGenericType (typeArgs);
  649. }
  650. #region LeftShift
  651. public static BinaryExpression LeftShift (Expression left, Expression right, MethodInfo method)
  652. {
  653. if (left == null)
  654. throw new ArgumentNullException ("left");
  655. if (right == null)
  656. throw new ArgumentNullException ("right");
  657. if (method != null)
  658. return new BinaryExpression(ExpressionType.LeftShift, left, right, method, method.ReturnType);
  659. // If the left side is any kind of integer and the right is int32 we don't have
  660. // to look for the "op_Addition" method.
  661. if (IsInteger(left.type) && right.type == typeof(Int32))
  662. return new BinaryExpression(ExpressionType.LeftShift, left, right, left.type);
  663. // Else we try for a user-defined operator.
  664. return GetUserDefinedBinaryOperatorOrThrow (ExpressionType.LeftShift, "op_LeftShift", left, right);
  665. }
  666. public static BinaryExpression LeftShift (Expression left, Expression right)
  667. {
  668. return LeftShift (left, right, null);
  669. }
  670. #endregion
  671. public static ListInitExpression ListInit(NewExpression newExpression, params ElementInit[] initializers)
  672. {
  673. if (initializers == null)
  674. throw new ArgumentNullException("inizializers");
  675. return ListInit(newExpression, Enumerable.ToReadOnlyCollection<ElementInit>(initializers));
  676. }
  677. public static ListInitExpression ListInit(NewExpression newExpression, IEnumerable<ElementInit> initializers)
  678. {
  679. if (newExpression == null)
  680. throw new ArgumentNullException("newExpression");
  681. if (initializers == null)
  682. throw new ArgumentNullException("inizializers");
  683. return new ListInitExpression(newExpression, Enumerable.ToReadOnlyCollection<ElementInit>(initializers));
  684. }
  685. public static MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable<MemberBinding> bindings)
  686. {
  687. if (newExpression == null)
  688. throw new ArgumentNullException("newExpression");
  689. if (bindings == null)
  690. throw new ArgumentNullException("bindings");
  691. return new MemberInitExpression(newExpression, Enumerable.ToReadOnlyCollection<MemberBinding>(bindings));
  692. }
  693. #region Modulo
  694. public static BinaryExpression Modulo (Expression left, Expression right, MethodInfo method)
  695. {
  696. if (left == null)
  697. throw new ArgumentNullException ("left");
  698. if (right == null)
  699. throw new ArgumentNullException ("right");
  700. if (method != null)
  701. return new BinaryExpression(ExpressionType.Modulo, left, right, method, method.ReturnType);
  702. // Since both the expressions define the same integer or boolean type we don't have
  703. // to look for the "op_BitwiseAnd" method.
  704. if (left.type == right.type && IsNumeric (left.type))
  705. return new BinaryExpression(ExpressionType.Modulo, left, right, left.type);
  706. // Else we try for a user-defined operator.
  707. return GetUserDefinedBinaryOperatorOrThrow (ExpressionType.Modulo, "op_Modulus", left, right);
  708. }
  709. public static BinaryExpression Modulo (Expression left, Expression right)
  710. {
  711. return Modulo (left, right, null);
  712. }
  713. #endregion
  714. #region Multiply
  715. public static BinaryExpression Multiply (Expression left, Expression right, MethodInfo method)
  716. {
  717. if (left == null)
  718. throw new ArgumentNullException ("left");
  719. if (right == null)
  720. throw new ArgumentNullException ("right");
  721. if (method != null)
  722. return new BinaryExpression(ExpressionType.Multiply, left, right, method, method.ReturnType);
  723. // Since both the expressions define the same integer or boolean type we don't have
  724. // to look for the "op_BitwiseAnd" method.
  725. if (left.type == right.type && IsNumeric (left.type))
  726. return new BinaryExpression(ExpressionType.Multiply, left, right, left.type);
  727. // Else we try for a user-defined operator.
  728. return GetUserDefinedBinaryOperatorOrThrow (ExpressionType.Multiply, "op_Multiply", left, right);
  729. }
  730. public static BinaryExpression Multiply (Expression left, Expression right)
  731. {
  732. return Multiply (left, right, null);
  733. }
  734. #endregion
  735. #region MultiplyChecked
  736. public static BinaryExpression MultiplyChecked (Expression left, Expression right, MethodInfo method)
  737. {
  738. if (left == null)
  739. throw new ArgumentNullException ("left");
  740. if (right == null)
  741. throw new ArgumentNullException ("right");
  742. if (method != null)
  743. return new BinaryExpression(ExpressionType.MultiplyChecked, left, right, method, method.ReturnType);
  744. // Since both the expressions define the same numeric type we don't have
  745. // to look for the "op_Addition" method.
  746. if (left.type == right.type && IsNumeric (left.type))
  747. return new BinaryExpression(ExpressionType.MultiplyChecked, left, right, left.type);
  748. method = GetUserDefinedBinaryOperator (left.type, right.type, "op_Multiply");
  749. if (method == null)
  750. throw new InvalidOperationException(String.Format(
  751. "The binary operator MultiplyChecked is not defined for the types '{0}' and '{1}'.", left.type, right.type));
  752. Type retType = method.ReturnType;
  753. return new BinaryExpression(ExpressionType.MultiplyChecked, left, right, method, retType);
  754. }
  755. public static BinaryExpression MultiplyChecked (Expression left, Expression right)
  756. {
  757. return MultiplyChecked(left, right, null);
  758. }
  759. #endregion
  760. #region Or
  761. public static BinaryExpression Or (Expression left, Expression right, MethodInfo method)
  762. {
  763. if (left == null)
  764. throw new ArgumentNullException ("left");
  765. if (right == null)
  766. throw new ArgumentNullException ("right");
  767. if (method != null)
  768. return new BinaryExpression(ExpressionType.Or, left, right, method, method.ReturnType);
  769. // Since both the expressions define the same integer or boolean type we don't have
  770. // to look for the "op_BitwiseOr" method.
  771. if (left.type == right.type && IsIntegerOrBool (left.type))
  772. return new BinaryExpression(ExpressionType.Or, left, right, left.type);
  773. // Else we try for a user-defined operator.
  774. return GetUserDefinedBinaryOperatorOrThrow (ExpressionType.Or, "op_BitwiseOr", left, right);
  775. }
  776. public static BinaryExpression Or (Expression left, Expression right)
  777. {
  778. return Or (left, right, null);
  779. }
  780. #endregion
  781. #region OrElse
  782. public static BinaryExpression OrElse (Expression left, Expression right, MethodInfo method)
  783. {
  784. if (left == null)
  785. throw new ArgumentNullException ("left");
  786. if (right == null)
  787. throw new ArgumentNullException ("right");
  788. // Since both the expressions define the same boolean type we don't have
  789. // to look for the "op_BitwiseOr" method.
  790. if (left.type == right.type && left.type == typeof(bool))
  791. return new BinaryExpression(ExpressionType.OrElse, left, right, left.type);
  792. // Else we must validate the method to make sure it has companion "true" and "false" operators.
  793. if (method == null)
  794. method = GetUserDefinedBinaryOperator (left.type, right.type, "op_BitwiseOr");
  795. if (method == null)
  796. throw new InvalidOperationException(String.Format(
  797. "The binary operator OrElse is not defined for the types '{0}' and '{1}'.", left.type, right.type));
  798. ValidateUserDefinedConditionalLogicOperator(ExpressionType.OrElse, left.type, right.type, method);
  799. return new BinaryExpression(ExpressionType.OrElse, left, right, method, method.ReturnType);
  800. }
  801. public static BinaryExpression OrElse (Expression left, Expression right)
  802. {
  803. return OrElse(left, right, null);
  804. }
  805. #endregion
  806. #region Property
  807. public static MemberExpression Property (Expression expression, MethodInfo propertyAccessor)
  808. {
  809. if (propertyAccessor == null)
  810. throw new ArgumentNullException("propertyAccessor");
  811. return Property(expression, GetProperty(propertyAccessor));
  812. }
  813. public static MemberExpression Property (Expression expression, PropertyInfo property)
  814. {
  815. if (property == null)
  816. throw new ArgumentNullException("property");
  817. Type propertyType;
  818. ValidateGettableFieldOrPropertyMember(property, out propertyType);
  819. return new MemberExpression(expression, property, propertyType);
  820. }
  821. public static MemberExpression Property(Expression expression, string propertyName)
  822. {
  823. if (expression == null)
  824. throw new ArgumentNullException ("expression");
  825. if (propertyName == null)
  826. throw new ArgumentNullException ("propertyName");
  827. PropertyInfo property = expression.Type.GetProperty (propertyName,
  828. BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
  829. if (property == null)
  830. throw new ArgumentException (String.Format ("{0} is not a member of type {1}",
  831. propertyName, expression.type.FullName));
  832. return Property (expression, property);
  833. }
  834. #endregion
  835. #region PropertyOrField
  836. public static MemberExpression PropertyOrField(Expression expression, string propertyOrFieldName)
  837. {
  838. if (expression == null)
  839. throw new ArgumentNullException ("expression");
  840. if (propertyOrFieldName == null)
  841. throw new ArgumentNullException ("propertyOrFieldName");
  842. PropertyInfo property = expression.type.GetProperty (propertyOrFieldName,
  843. BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
  844. if (property != null)
  845. return Property (expression, property);
  846. FieldInfo field = expression.type.GetField (propertyOrFieldName,
  847. BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
  848. if (field != null)
  849. return Field (expression, field);
  850. throw new ArgumentException (String.Format ("{0} is not a member of type {1}",
  851. propertyOrFieldName, expression.type.FullName));
  852. }
  853. #endregion
  854. #region Quote
  855. public static UnaryExpression Quote(Expression expression)
  856. {
  857. if (expression == null)
  858. throw new ArgumentNullException ("expression");
  859. return new UnaryExpression (ExpressionType.Quote, expression, expression.GetType());
  860. }
  861. #endregion
  862. #region RightShift
  863. public static BinaryExpression RightShift (Expression left, Expression right, MethodInfo method)
  864. {
  865. if (left == null)
  866. throw new ArgumentNullException ("left");
  867. if (right == null)
  868. throw new ArgumentNullException ("right");
  869. if (method != null)
  870. return new BinaryExpression (ExpressionType.RightShift, left, right, method, method.ReturnType);
  871. // If the left side is any kind of integer and the right is int32 we don't have
  872. // to look for the "op_Addition" method.
  873. if (IsInteger(left.type) && right.type == typeof(Int32))
  874. return new BinaryExpression (ExpressionType.RightShift, left, right, left.type);
  875. // Else we try for a user-defined operator.
  876. return GetUserDefinedBinaryOperatorOrThrow (ExpressionType.RightShift, "op_RightShift", left, right);
  877. }
  878. public static BinaryExpression RightShift (Expression left, Expression right)
  879. {
  880. return RightShift (left, right, null);
  881. }
  882. #endregion
  883. #region Subtract
  884. public static BinaryExpression Subtract (Expression left, Expression right, MethodInfo method)
  885. {
  886. if (left == null)
  887. throw new ArgumentNullException ("left");
  888. if (right == null)
  889. throw new ArgumentNullException ("right");
  890. if (method != null)
  891. return new BinaryExpression (ExpressionType.Subtract, left, right, method, method.ReturnType);
  892. // Since both the expressions define the same numeric type we don't have
  893. // to look for the "op_Addition" method.
  894. if (left.type == right.type && IsNumeric (left.type))
  895. return new BinaryExpression (ExpressionType.Subtract, left, right, left.type);
  896. // Else we try for a user-defined operator.
  897. return GetUserDefinedBinaryOperatorOrThrow (ExpressionType.Subtract, "op_Subtraction", left, right);
  898. }
  899. public static BinaryExpression Subtract (Expression left, Expression right)
  900. {
  901. return Subtract (left, right, null);
  902. }
  903. #endregion
  904. #region SubtractChecked
  905. public static BinaryExpression SubtractChecked (Expression left, Expression right, MethodInfo method)
  906. {
  907. if (left == null)
  908. throw new ArgumentNullException ("left");
  909. if (right == null)
  910. throw new ArgumentNullException ("right");
  911. if (method != null)
  912. return new BinaryExpression (ExpressionType.SubtractChecked, left, right, method, method.ReturnType);
  913. // Since both the expressions define the same numeric type we don't have
  914. // to look for the "op_Addition" method.
  915. if (left.type == right.type && IsNumeric (left.type))
  916. return new BinaryExpression (ExpressionType.SubtractChecked, left, right, left.type);
  917. method = GetUserDefinedBinaryOperator (left.type, right.type, "op_Subtraction");
  918. if (method == null)
  919. throw new InvalidOperationException (String.Format (
  920. "The binary operator AddChecked is not defined for the types '{0}' and '{1}'.", left.type, right.type));
  921. Type retType = method.ReturnType;
  922. return new BinaryExpression (ExpressionType.SubtractChecked, left, right, method, retType);
  923. }
  924. public static BinaryExpression SubtractChecked (Expression left, Expression right)
  925. {
  926. return SubtractChecked (left, right, null);
  927. }
  928. #endregion
  929. #region TypeAs
  930. public static UnaryExpression TypeAs (Expression expression, Type type)
  931. {
  932. if (expression == null)
  933. throw new ArgumentNullException ("expression");
  934. if (type == null)
  935. throw new ArgumentNullException ("type");
  936. if (type.IsValueType && !IsNullableType (type))
  937. throw new ArgumentException ("Reference or nullable type expected");
  938. return new UnaryExpression (ExpressionType.TypeAs, expression, type);
  939. }
  940. #endregion
  941. #region TypeIs
  942. public static TypeBinaryExpression TypeIs (Expression expression, Type type)
  943. {
  944. if (expression == null)
  945. throw new ArgumentNullException ("expression");
  946. if (type == null)
  947. throw new ArgumentNullException ("type");
  948. return new TypeBinaryExpression (ExpressionType.TypeIs, expression, type, typeof(bool));
  949. }
  950. #endregion
  951. [MonoTODO]
  952. public static Expression<TDelegate> Lambda<TDelegate> (Expression body, params ParameterExpression [] parameters)
  953. {
  954. throw new NotImplementedException ();
  955. }
  956. [MonoTODO]
  957. public static Expression<TDelegate> Lambda<TDelegate> (Expression body, IEnumerable<ParameterExpression> parameters)
  958. {
  959. throw new NotImplementedException ();
  960. }
  961. [MonoTODO]
  962. public static LambdaExpression Lambda (Expression body, params ParameterExpression [] parameters)
  963. {
  964. throw new NotImplementedException ();
  965. }
  966. [MonoTODO]
  967. public static LambdaExpression Lambda (Type delegateType, Expression body, params ParameterExpression [] parameters)
  968. {
  969. throw new NotImplementedException ();
  970. }
  971. [MonoTODO]
  972. public static LambdaExpression Lambda (Type delegateType, Expression body, IEnumerable<ParameterExpression> parameters)
  973. {
  974. throw new NotImplementedException ();
  975. }
  976. }
  977. }