Linq 动态查询排序
Linq的排序一般是这样写的:
query.OrderBy(x => x.Tel).Skip(0).Take(10);
实际使用中排序字段可能是通过字符类型的参数来设置的,于是想这样实现:
query.OrderBy(x=>x.GetType().GetField("Tel")).Skip(0).Take(10);
上面的写法是无法编译通过的,此路不通,于是找到一个order扩展类:
1 using System; 2 using System.Linq; 3 using System.Linq.Expressions; 4 using System.Collections.Generic; 5 using System.Reflection; 6 7 public static class OrderExtension 8 { 9 public static IOrderedQueryable<T> DynamicOrderBy<T>(this IQueryable<T> source, string property) 10 { 11 return ApplyOrder<T>(source, property, "OrderBy"); 12 } 13 14 public static IOrderedQueryable<T> DynamicOrderByDescending<T>(this IQueryable<T> source, string property) 15 { 16 return ApplyOrder<T>(source, property, "OrderByDescending"); 17 } 18 19 public static IOrderedQueryable<T> DynamicThenBy<T>(this IOrderedQueryable<T> source, string property) 20 { 21 return ApplyOrder<T>(source, property, "ThenBy"); 22 } 23 24 public static IOrderedQueryable<T> DynamicThenByDescending<T>(this IOrderedQueryable<T> source, string property) 25 { 26 return ApplyOrder<T>(source, property, "ThenByDescending"); 27 } 28 29 static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName) 30 { 31 string[] props = property.Split('.'); 32 Type type = typeof(T); ParameterExpression arg = Expression.Parameter(type, "x"); 33 Expression expr = arg; foreach (string prop in props) 34 { 35 // use reflection (not ComponentModel) to mirror LINQ 36 PropertyInfo pi = type.GetProperty(prop); expr = Expression.Property(expr, pi); type = pi.PropertyType; 37 } 38 Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type); 39 LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg); 40 object result = typeof(Queryable).GetMethods().Single(method => method.Name == methodName 41 && method.IsGenericMethodDefinition 42 && method.GetGenericArguments().Length == 2 43 && method.GetParameters().Length == 2) 44 .MakeGenericMethod(typeof(T), type) 45 .Invoke(null, new object[] { source, lambda }); 46 return (IOrderedQueryable<T>)result; 47 } 48 }
排序可以这样处理了:
query.DynamicOrderBy("Tel").Skip(0).Take(10);
排序问题解决了,查询还是没解决,MongoDb Linq的查询语句一般是这样的:
var query = collection.AsQueryable<Customer>().Where(e => e.CustomerName == searchWord);
如果将where条件分开写是这样的:
System.Linq.Expressions.Expression<Func<Customer, bool>> expression = e => e.CustomerName == searchWord; var query = collection.AsQueryable<Customer>().Where(expression);
那么问题来了,我想动态添加个where条件是否可以这样写呢:
System.Linq.Expressions.Expression<Func<Customer, bool>> expression = e => e.CustomerName == searchWord; expression += e => e.Tel == "123456789"; var query = collection.AsQueryable<Customer>().Where(expression);
这样想当然的代码当然不能编译通过了~
继续google,找到一个PredicateBuilder类:
1 using System; 2 using System.Linq; 3 using System.Linq.Expressions; 4 using System.Collections.Generic; 5 6 using System.Reflection; 7 using MongoDB.Driver.Linq; 8 9 public static class PredicateBuilder 10 { 11 public static Expression<Func<T, bool>> True<T>() { return f => true; } 12 public static Expression<Func<T, bool>> False<T>() { return f => false; } 13 public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge) 14 { 15 // build parameter map (from parameters of second to parameters of first) 16 var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f); 17 18 // replace parameters in the second lambda expression with parameters from the first 19 var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); 20 21 // apply composition of lambda expression bodies to parameters from the first expression 22 return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters); 23 } 24 25 public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) 26 { 27 return first.Compose(second, Expression.And); 28 } 29 public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) 30 { 31 return first.Compose(second, Expression.Or); 32 } 33 34 public static object GetPropertyValue(object obj, string property) 35 { 36 System.Reflection.PropertyInfo propertyInfo = obj.GetType().GetProperty(property); 37 return propertyInfo.GetValue(obj, null); 38 } 39 } 40 41 public class ParameterRebinder : ExpressionVisitor 42 { 43 private readonly Dictionary<ParameterExpression, ParameterExpression> map; 44 45 public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map) 46 { 47 this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>(); 48 } 49 50 public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp) 51 { 52 return new ParameterRebinder(map).Visit(exp); 53 } 54 55 protected override Expression VisitParameter(ParameterExpression p) 56 { 57 ParameterExpression replacement; 58 if (map.TryGetValue(p, out replacement)) 59 { 60 p = replacement; 61 } 62 return base.VisitParameter(p); 63 } 64 }
于是查询可以这样写了:
//Linq 强类型动态查询 System.Linq.Expressions.Expression<Func<Customer, bool>> expression = PredicateBuilder.True<Customer>(); if (!string.IsNullOrEmpty(searchWord)) { expression = expression.And(e => e.CustomerName == searchWord); expression = expression.And(e => e.Tel != ""); expression = expression.And(e => e.ContactName.Contains("ContactName")); } var query = collection.AsQueryable<Customer>().Where(expression);
另一个PredicateBuilder类用不了,具体原因没去分析了
1 public static class PredicateBuilder 2 { 3 public static Expression<Func<T, bool>> True<T>() { return f => true; } 4 public static Expression<Func<T, bool>> False<T>() { return f => false; } 5 6 public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2) 7 { 8 var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>()); 9 return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters); 10 } 11 12 public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2) 13 { 14 var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>()); 15 return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters); 16 } 17 }
还有Dynamic LINQ可以参考: http://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library
1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.Linq; 5 using System.Linq.Expressions; 6 using System.Reflection; 7 using System.Reflection.Emit; 8 using System.Threading; 9 10 namespace System.Linq.Dynamic 11 { 12 public static class DynamicQueryable 13 { 14 public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values) 15 { 16 return (IQueryable<T>)Where((IQueryable)source, predicate, values); 17 } 18 19 public static IQueryable Where(this IQueryable source, string predicate, params object[] values) 20 { 21 if (source == null) throw new ArgumentNullException("source"); 22 if (predicate == null) throw new ArgumentNullException("predicate"); 23 LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, values); 24 return source.Provider.CreateQuery( 25 Expression.Call( 26 typeof(Queryable), "Where", 27 new Type[] { source.ElementType }, 28 source.Expression, Expression.Quote(lambda))); 29 } 30 31 public static IQueryable Select(this IQueryable source, string selector, params object[] values) 32 { 33 if (source == null) throw new ArgumentNullException("source"); 34 if (selector == null) throw new ArgumentNullException("selector"); 35 LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, null, selector, values); 36 return source.Provider.CreateQuery( 37 Expression.Call( 38 typeof(Queryable), "Select", 39 new Type[] { source.ElementType, lambda.Body.Type }, 40 source.Expression, Expression.Quote(lambda))); 41 } 42 43 public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values) 44 { 45 return (IQueryable<T>)OrderBy((IQueryable)source, ordering, values); 46 } 47 48 public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values) 49 { 50 if (source == null) throw new ArgumentNullException("source"); 51 if (ordering == null) throw new ArgumentNullException("ordering"); 52 ParameterExpression[] parameters = new ParameterExpression[] { 53 Expression.Parameter(source.ElementType, "") }; 54 ExpressionParser parser = new ExpressionParser(parameters, ordering, values); 55 IEnumerable<DynamicOrdering> orderings = parser.ParseOrdering(); 56 Expression queryExpr = source.Expression; 57 string methodAsc = "OrderBy"; 58 string methodDesc = "OrderByDescending"; 59 foreach (DynamicOrdering o in orderings) 60 { 61 queryExpr = Expression.Call( 62 typeof(Queryable), o.Ascending ? methodAsc : methodDesc, 63 new Type[] { source.ElementType, o.Selector.Type }, 64 queryExpr, Expression.Quote(Expression.Lambda(o.Selector, parameters))); 65 methodAsc = "ThenBy"; 66 methodDesc = "ThenByDescending"; 67 } 68 return source.Provider.CreateQuery(queryExpr); 69 } 70 71 public static IQueryable Take(this IQueryable source, int count) 72 { 73 if (source == null) throw new ArgumentNullException("source"); 74 return source.Provider.CreateQuery( 75 Expression.Call( 76 typeof(Queryable), "Take", 77 new Type[] { source.ElementType }, 78 source.Expression, Expression.Constant(count))); 79 } 80 81 public static IQueryable Skip(this IQueryable source, int count) 82 { 83 if (source == null) throw new ArgumentNullException("source"); 84 return source.Provider.CreateQuery( 85 Expression.Call( 86 typeof(Queryable), "Skip", 87 new Type[] { source.ElementType }, 88 source.Expression, Expression.Constant(count))); 89 } 90 91 public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, params object[] values) 92 { 93 if (source == null) throw new ArgumentNullException("source"); 94 if (keySelector == null) throw new ArgumentNullException("keySelector"); 95 if (elementSelector == null) throw new ArgumentNullException("elementSelector"); 96 LambdaExpression keyLambda = DynamicExpression.ParseLambda(source.ElementType, null, keySelector, values); 97 LambdaExpression elementLambda = DynamicExpression.ParseLambda(source.ElementType, null, elementSelector, values); 98 return source.Provider.CreateQuery( 99 Expression.Call( 100 typeof(Queryable), "GroupBy", 101 new Type[] { source.ElementType, keyLambda.Body.Type, elementLambda.Body.Type }, 102 source.Expression, Expression.Quote(keyLambda), Expression.Quote(elementLambda))); 103 } 104 105 public static bool Any(this IQueryable source) 106 { 107 if (source == null) throw new ArgumentNullException("source"); 108 return (bool)source.Provider.Execute( 109 Expression.Call( 110 typeof(Queryable), "Any", 111 new Type[] { source.ElementType }, source.Expression)); 112 } 113 114 public static int Count(this IQueryable source) 115 { 116 if (source == null) throw new ArgumentNullException("source"); 117 return (int)source.Provider.Execute( 118 Expression.Call( 119 typeof(Queryable), "Count", 120 new Type[] { source.ElementType }, source.Expression)); 121 } 122 } 123 124 public abstract class DynamicClass 125 { 126 public override string ToString() 127 { 128 PropertyInfo[] props = this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); 129 StringBuilder sb = new StringBuilder(); 130 sb.Append("{"); 131 for (int i = 0; i < props.Length; i++) 132 { 133 if (i > 0) sb.Append(", "); 134 sb.Append(props[i].Name); 135 sb.Append("="); 136 sb.Append(props[i].GetValue(this, null)); 137 } 138 sb.Append("}"); 139 return sb.ToString(); 140 } 141 } 142 143 public class DynamicProperty 144 { 145 string name; 146 Type type; 147 148 public DynamicProperty(string name, Type type) 149 { 150 if (name == null) throw new ArgumentNullException("name"); 151 if (type == null) throw new ArgumentNullException("type"); 152 this.name = name; 153 this.type = type; 154 } 155 156 public string Name 157 { 158 get { return name; } 159 } 160 161 public Type Type 162 { 163 get { return type; } 164 } 165 } 166 167 public static class DynamicExpression 168 { 169 public static Expression Parse(Type resultType, string expression, params object[] values) 170 { 171 ExpressionParser parser = new ExpressionParser(null, expression, values); 172 return parser.Parse(resultType); 173 } 174 175 public static LambdaExpression ParseLambda(Type itType, Type resultType, string expression, params object[] values) 176 { 177 return ParseLambda(new ParameterExpression[] { Expression.Parameter(itType, "") }, resultType, expression, values); 178 } 179 180 public static LambdaExpression ParseLambda(ParameterExpression[] parameters, Type resultType, string expression, params object[] values) 181 { 182 ExpressionParser parser = new ExpressionParser(parameters, expression, values); 183 return Expression.Lambda(parser.Parse(resultType), parameters); 184 } 185 186 public static Expression<Func<T, S>> ParseLambda<T, S>(string expression, params object[] values) 187 { 188 return (Expression<Func<T, S>>)ParseLambda(typeof(T), typeof(S), expression, values); 189 } 190 191 public static Type CreateClass(params DynamicProperty[] properties) 192 { 193 return ClassFactory.Instance.GetDynamicClass(properties); 194 } 195 196 public static Type CreateClass(IEnumerable<DynamicProperty> properties) 197 { 198 return ClassFactory.Instance.GetDynamicClass(properties); 199 } 200 } 201 202 internal class DynamicOrdering 203 { 204 public Expression Selector; 205 public bool Ascending; 206 } 207 208 internal class Signature : IEquatable<Signature> 209 { 210 public DynamicProperty[] properties; 211 public int hashCode; 212 213 public Signature(IEnumerable<DynamicProperty> properties) 214 { 215 this.properties = properties.ToArray(); 216 hashCode = 0; 217 foreach (DynamicProperty p in properties) 218 { 219 hashCode ^= p.Name.GetHashCode() ^ p.Type.GetHashCode(); 220 } 221 } 222 223 public override int GetHashCode() 224 { 225 return hashCode; 226 } 227 228 public override bool Equals(object obj) 229 { 230 return obj is Signature ? Equals((Signature)obj) : false; 231 } 232 233 public bool Equals(Signature other) 234 { 235 if (properties.Length != other.properties.Length) return false; 236 for (int i = 0; i < properties.Length; i++) 237 { 238 if (properties[i].Name != other.properties[i].Name || 239 properties[i].Type != other.properties[i].Type) return false; 240 } 241 return true; 242 } 243 } 244 245 internal class ClassFactory 246 { 247 public static readonly ClassFactory Instance = new ClassFactory(); 248 249 static ClassFactory() { } // Trigger lazy initialization of static fields 250 251 ModuleBuilder module; 252 Dictionary<Signature, Type> classes; 253 int classCount; 254 ReaderWriterLock rwLock; 255 256 private ClassFactory() 257 { 258 AssemblyName name = new AssemblyName("DynamicClasses"); 259 AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); 260 #if ENABLE_LINQ_PARTIAL_TRUST 261 new ReflectionPermission(PermissionState.Unrestricted).Assert(); 262 #endif 263 try 264 { 265 module = assembly.DefineDynamicModule("Module"); 266 } 267 finally 268 { 269 #if ENABLE_LINQ_PARTIAL_TRUST 270 PermissionSet.RevertAssert(); 271 #endif 272 } 273 classes = new Dictionary<Signature, Type>(); 274 rwLock = new ReaderWriterLock(); 275 } 276 277 public Type GetDynamicClass(IEnumerable<DynamicProperty> properties) 278 { 279 rwLock.AcquireReaderLock(Timeout.Infinite); 280 try 281 { 282 Signature signature = new Signature(properties); 283 Type type; 284 if (!classes.TryGetValue(signature, out type)) 285 { 286 type = CreateDynamicClass(signature.properties); 287 classes.Add(signature, type); 288 } 289 return type; 290 } 291 finally 292 { 293 rwLock.ReleaseReaderLock(); 294 } 295 } 296 297 Type CreateDynamicClass(DynamicProperty[] properties) 298 { 299 LockCookie cookie = rwLock.UpgradeToWriterLock(Timeout.Infinite); 300 try 301 { 302 string typeName = "DynamicClass" + (classCount + 1); 303 #if ENABLE_LINQ_PARTIAL_TRUST 304 new ReflectionPermission(PermissionState.Unrestricted).Assert(); 305 #endif 306 try 307 { 308 TypeBuilder tb = this.module.DefineType(typeName, TypeAttributes.Class | 309 TypeAttributes.Public, typeof(DynamicClass)); 310 FieldInfo[] fields = GenerateProperties(tb, properties); 311 GenerateEquals(tb, fields); 312 GenerateGetHashCode(tb, fields); 313 Type result = tb.CreateType(); 314 classCount++; 315 return result; 316 } 317 finally 318 { 319 #if ENABLE_LINQ_PARTIAL_TRUST 320 PermissionSet.RevertAssert(); 321 #endif 322 } 323 } 324 finally 325 { 326 rwLock.DowngradeFromWriterLock(ref cookie); 327 } 328 } 329 330 FieldInfo[] GenerateProperties(TypeBuilder tb, DynamicProperty[] properties) 331 { 332 FieldInfo[] fields = new FieldBuilder[properties.Length]; 333 for (int i = 0; i < properties.Length; i++) 334 { 335 DynamicProperty dp = properties[i]; 336 FieldBuilder fb = tb.DefineField("_" + dp.Name, dp.Type, FieldAttributes.Private); 337 PropertyBuilder pb = tb.DefineProperty(dp.Name, PropertyAttributes.HasDefault, dp.Type, null); 338 MethodBuilder mbGet = tb.DefineMethod("get_" + dp.Name, 339 MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, 340 dp.Type, Type.EmptyTypes); 341 ILGenerator genGet = mbGet.GetILGenerator(); 342 genGet.Emit(OpCodes.Ldarg_0); 343 genGet.Emit(OpCodes.Ldfld, fb); 344 genGet.Emit(OpCodes.Ret); 345 MethodBuilder mbSet = tb.DefineMethod("set_" + dp.Name, 346 MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, 347 null, new Type[] { dp.Type }); 348 ILGenerator genSet = mbSet.GetILGenerator(); 349 genSet.Emit(OpCodes.Ldarg_0); 350 genSet.Emit(OpCodes.Ldarg_1); 351 genSet.Emit(OpCodes.Stfld, fb); 352 genSet.Emit(OpCodes.Ret); 353 pb.SetGetMethod(mbGet); 354 pb.SetSetMethod(mbSet); 355 fields[i] = fb; 356 } 357 return fields; 358 } 359 360 void GenerateEquals(TypeBuilder tb, FieldInfo[] fields) 361 { 362 MethodBuilder mb = tb.DefineMethod("Equals", 363 MethodAttributes.Public | MethodAttributes.ReuseSlot | 364 MethodAttributes.Virtual | MethodAttributes.HideBySig, 365 typeof(bool), new Type[] { typeof(object) }); 366 ILGenerator gen = mb.GetILGenerator(); 367 LocalBuilder other = gen.DeclareLocal(tb); 368 Label next = gen.DefineLabel(); 369 gen.Emit(OpCodes.Ldarg_1); 370 gen.Emit(OpCodes.Isinst, tb); 371 gen.Emit(OpCodes.Stloc, other); 372 gen.Emit(OpCodes.Ldloc, other); 373 gen.Emit(OpCodes.Brtrue_S, next); 374 gen.Emit(OpCodes.Ldc_I4_0); 375 gen.Emit(OpCodes.Ret); 376 gen.MarkLabel(next); 377 foreach (FieldInfo field in fields) 378 { 379 Type ft = field.FieldType; 380 Type ct = typeof(EqualityComparer<>).MakeGenericType(ft); 381 next = gen.DefineLabel(); 382 gen.EmitCall(OpCodes.Call, ct.GetMethod("get_Default"), null); 383 gen.Emit(OpCodes.Ldarg_0); 384 gen.Emit(OpCodes.Ldfld, field); 385 gen.Emit(OpCodes.Ldloc, other); 386 gen.Emit(OpCodes.Ldfld, field); 387 gen.EmitCall(OpCodes.Callvirt, ct.GetMethod("Equals", new Type[] { ft, ft }), null); 388 gen.Emit(OpCodes.Brtrue_S, next); 389 gen.Emit(OpCodes.Ldc_I4_0); 390 gen.Emit(OpCodes.Ret); 391 gen.MarkLabel(next); 392 } 393 gen.Emit(OpCodes.Ldc_I4_1); 394 gen.Emit(OpCodes.Ret); 395 } 396 397 void GenerateGetHashCode(TypeBuilder tb, FieldInfo[] fields) 398 { 399 MethodBuilder mb = tb.DefineMethod("GetHashCode", 400 MethodAttributes.Public | MethodAttributes.ReuseSlot | 401 MethodAttributes.Virtual | MethodAttributes.HideBySig, 402 typeof(int), Type.EmptyTypes); 403 ILGenerator gen = mb.GetILGenerator(); 404 gen.Emit(OpCodes.Ldc_I4_0); 405 foreach (FieldInfo field in fields) 406 { 407 Type ft = field.FieldType; 408 Type ct = typeof(EqualityComparer<>).MakeGenericType(ft); 409 gen.EmitCall(OpCodes.Call, ct.GetMethod("get_Default"), null); 410 gen.Emit(OpCodes.Ldarg_0); 411 gen.Emit(OpCodes.Ldfld, field); 412 gen.EmitCall(OpCodes.Callvirt, ct.GetMethod("GetHashCode", new Type[] { ft }), null); 413 gen.Emit(OpCodes.Xor); 414 } 415 gen.Emit(OpCodes.Ret); 416 } 417 } 418 419 public sealed class ParseException : Exception 420 { 421 int position; 422 423 public ParseException(string message, int position) 424 : base(message) 425 { 426 this.position = position; 427 } 428 429 public int Position 430 { 431 get { return position; } 432 } 433 434 public override string ToString() 435 { 436 return string.Format(Res.ParseExceptionFormat, Message, position); 437 } 438 } 439 440 internal class ExpressionParser 441 { 442 struct Token 443 { 444 public TokenId id; 445 public string text; 446 public int pos; 447 } 448 449 enum TokenId 450 { 451 Unknown, 452 End, 453 Identifier, 454 StringLiteral, 455 IntegerLiteral, 456 RealLiteral, 457 Exclamation, 458 Percent, 459 Amphersand, 460 OpenParen, 461 CloseParen, 462 Asterisk, 463 Plus, 464 Comma, 465 Minus, 466 Dot, 467 Slash, 468 Colon, 469 LessThan, 470 Equal, 471 GreaterThan, 472 Question, 473 OpenBracket, 474 CloseBracket, 475 Bar, 476 ExclamationEqual, 477 DoubleAmphersand, 478 LessThanEqual, 479 LessGreater, 480 DoubleEqual, 481 GreaterThanEqual, 482 DoubleBar 483 } 484 485 interface ILogicalSignatures 486 { 487 void F(bool x, bool y); 488 void F(bool? x, bool? y); 489 } 490 491 interface IArithmeticSignatures 492 { 493 void F(int x, int y); 494 void F(uint x, uint y); 495 void F(long x, long y); 496 void F(ulong x, ulong y); 497 void F(float x, float y); 498 void F(double x, double y); 499 void F(decimal x, decimal y); 500 void F(int? x, int? y); 501 void F(uint? x, uint? y); 502 void F(long? x, long? y); 503 void F(ulong? x, ulong? y); 504 void F(float? x, float? y); 505 void F(double? x, double? y); 506 void F(decimal? x, decimal? y); 507 } 508 509 interface IRelationalSignatures : IArithmeticSignatures 510 { 511 void F(string x, string y); 512 void F(char x, char y); 513 void F(DateTime x, DateTime y); 514 void F(TimeSpan x, TimeSpan y); 515 void F(char? x, char? y); 516 void F(DateTime? x, DateTime? y); 517 void F(TimeSpan? x, TimeSpan? y); 518 } 519 520 interface IEqualitySignatures : IRelationalSignatures 521 { 522 void F(bool x, bool y); 523 void F(bool? x, bool? y); 524 } 525 526 interface IAddSignatures : IArithmeticSignatures 527 { 528 void F(DateTime x, TimeSpan y); 529 void F(TimeSpan x, TimeSpan y); 530 void F(DateTime? x, TimeSpan? y); 531 void F(TimeSpan? x, TimeSpan? y); 532 } 533 534 interface ISubtractSignatures : IAddSignatures 535 { 536 void F(DateTime x, DateTime y); 537 void F(DateTime? x, DateTime? y); 538 } 539 540 interface INegationSignatures 541 { 542 void F(int x); 543 void F(long x); 544 void F(float x); 545 void F(double x); 546 void F(decimal x); 547 void F(int? x); 548 void F(long? x); 549 void F(float? x); 550 void F(double? x); 551 void F(decimal? x); 552 } 553 554 interface INotSignatures 555 { 556 void F(bool x); 557 void F(bool? x); 558 } 559 560 interface IEnumerableSignatures 561 { 562 void Where(bool predicate); 563 void Any(); 564 void Any(bool predicate); 565 void All(bool predicate); 566 void Count(); 567 void Count(bool predicate); 568 void Min(object selector); 569 void Max(object selector); 570 void Sum(int selector); 571 void Sum(int? selector); 572 void Sum(long selector); 573 void Sum(long? selector); 574 void Sum(float selector); 575 void Sum(float? selector); 576 void Sum(double selector); 577 void Sum(double? selector); 578 void Sum(decimal selector); 579 void Sum(decimal? selector); 580 void Average(int selector); 581 void Average(int? selector); 582 void Average(long selector); 583 void Average(long? selector); 584 void Average(float selector); 585 void Average(float? selector); 586 void Average(double selector); 587 void Average(double? selector); 588 void Average(decimal selector); 589 void Average(decimal? selector); 590 } 591 592 static readonly Type[] predefinedTypes = { 593 typeof(Object), 594 typeof(Boolean), 595 typeof(Char), 596 typeof(String), 597 typeof(SByte), 598 typeof(Byte), 599 typeof(Int16), 600 typeof(UInt16), 601 typeof(Int32), 602 typeof(UInt32), 603 typeof(Int64), 604 typeof(UInt64), 605 typeof(Single), 606 typeof(Double), 607 typeof(Decimal), 608 typeof(DateTime), 609 typeof(TimeSpan), 610 typeof(Guid), 611 typeof(Math), 612 typeof(Convert) 613 }; 614 615 static readonly Expression trueLiteral = Expression.Constant(true); 616 static readonly Expression falseLiteral = Expression.Constant(false); 617 static readonly Expression nullLiteral = Expression.Constant(null); 618 619 static readonly string keywordIt = "it"; 620 static readonly string keywordIif = "iif"; 621 static readonly string keywordNew = "new"; 622 623 static Dictionary<string, object> keywords; 624 625 Dictionary<string, object> symbols; 626 IDictionary<string, object> externals; 627 Dictionary<Expression, string> literals; 628 ParameterExpression it; 629 string text; 630 int textPos; 631 int textLen; 632 char ch; 633 Token token; 634 635 public ExpressionParser(ParameterExpression[] parameters, string expression, object[] values) 636 { 637 if (expression == null) throw new ArgumentNullException("expression"); 638 if (keywords == null) keywords = CreateKeywords(); 639 symbols = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); 640 literals = new Dictionary<Expression, string>(); 641 if (parameters != null) ProcessParameters(parameters); 642 if (values != null) ProcessValues(values); 643 text = expression; 644 textLen = text.Length; 645 SetTextPos(0); 646 NextToken(); 647 } 648 649 void ProcessParameters(ParameterExpression[] parameters) 650 { 651 foreach (ParameterExpression pe in parameters) 652 if (!String.IsNullOrEmpty(pe.Name)) 653 AddSymbol(pe.Name, pe); 654 if (parameters.Length == 1 && String.IsNullOrEmpty(parameters[0].Name)) 655 it = parameters[0]; 656 } 657 658 void ProcessValues(object[] values) 659 { 660 for (int i = 0; i < values.Length; i++) 661 { 662 object value = values[i]; 663 if (i == values.Length - 1 && value is IDictionary<string, object>) 664 { 665 externals = (IDictionary<string, object>)value; 666 } 667 else 668 { 669 AddSymbol("@" + i.ToString(System.Globalization.CultureInfo.InvariantCulture), value); 670 } 671 } 672 } 673 674 void AddSymbol(string name, object value) 675 { 676 if (symbols.ContainsKey(name)) 677 throw ParseError(Res.DuplicateIdentifier, name); 678 symbols.Add(name, value); 679 } 680 681 public Expression Parse(Type resultType) 682 { 683 int exprPos = token.pos; 684 Expression expr = ParseExpression(); 685 if (resultType != null) 686 if ((expr = PromoteExpression(expr, resultType, true)) == null) 687 throw ParseError(exprPos, Res.ExpressionTypeMismatch, GetTypeName(resultType)); 688 ValidateToken(TokenId.End, Res.SyntaxError); 689 return expr; 690 } 691 692 #pragma warning disable 0219 693 public IEnumerable<DynamicOrdering> ParseOrdering() 694 { 695 List<DynamicOrdering> orderings = new List<DynamicOrdering>(); 696 while (true) 697 { 698 Expression expr = ParseExpression(); 699 bool ascending = true; 700 if (TokenIdentifierIs("asc") || TokenIdentifierIs("ascending")) 701 { 702 NextToken(); 703 } 704 else if (TokenIdentifierIs("desc") || TokenIdentifierIs("descending")) 705 { 706 NextToken(); 707 ascending = false; 708 } 709 orderings.Add(new DynamicOrdering { Selector = expr, Ascending = ascending }); 710 if (token.id != TokenId.Comma) break; 711 NextToken(); 712 } 713 ValidateToken(TokenId.End, Res.SyntaxError); 714 return orderings; 715 } 716 #pragma warning restore 0219 717 718 // ?: operator 719 Expression ParseExpression() 720 { 721 int errorPos = token.pos; 722 Expression expr = ParseLogicalOr(); 723 if (token.id == TokenId.Question) 724 { 725 NextToken(); 726 Expression expr1 = ParseExpression(); 727 ValidateToken(TokenId.Colon, Res.ColonExpected); 728 NextToken(); 729 Expression expr2 = ParseExpression(); 730 expr = GenerateConditional(expr, expr1, expr2, errorPos); 731 } 732 return expr; 733 } 734 735 // ||, or operator 736 Expression ParseLogicalOr() 737 { 738 Expression left = ParseLogicalAnd(); 739 while (token.id == TokenId.DoubleBar || TokenIdentifierIs("or")) 740 { 741 Token op = token; 742 NextToken(); 743 Expression right = ParseLogicalAnd(); 744 CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos); 745 left = Expression.OrElse(left, right); 746 } 747 return left; 748 } 749 750 // &&, and operator 751 Expression ParseLogicalAnd() 752 { 753 Expression left = ParseComparison(); 754 while (token.id == TokenId.DoubleAmphersand || TokenIdentifierIs("and")) 755 { 756 Token op = token; 757 NextToken(); 758 Expression right = ParseComparison(); 759 CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos); 760 left = Expression.AndAlso(left, right); 761 } 762 return left; 763 } 764 765 // =, ==, !=, <>, >, >=, <, <= operators 766 Expression ParseComparison() 767 { 768 Expression left = ParseAdditive(); 769 while (token.id == TokenId.Equal || token.id == TokenId.DoubleEqual || 770 token.id == TokenId.ExclamationEqual || token.id == TokenId.LessGreater || 771 token.id == TokenId.GreaterThan || token.id == TokenId.GreaterThanEqual || 772 token.id == TokenId.LessThan || token.id == TokenId.LessThanEqual) 773 { 774 Token op = token; 775 NextToken(); 776 Expression right = ParseAdditive(); 777 bool isEquality = op.id == TokenId.Equal || op.id == TokenId.DoubleEqual || 778 op.id == TokenId.ExclamationEqual || op.id == TokenId.LessGreater; 779 if (isEquality && !left.Type.IsValueType && !right.Type.IsValueType) 780 { 781 if (left.Type != right.Type) 782 { 783 if (left.Type.IsAssignableFrom(right.Type)) 784 { 785 right = Expression.Convert(right, left.Type); 786 } 787 else if (right.Type.IsAssignableFrom(left.Type)) 788 { 789 left = Expression.Convert(left, right.Type); 790 } 791 else 792 { 793 throw IncompatibleOperandsError(op.text, left, right, op.pos); 794 } 795 } 796 } 797 else if (IsEnumType(left.Type) || IsEnumType(right.Type)) 798 { 799 if (left.Type != right.Type) 800 { 801 Expression e; 802 if ((e = PromoteExpression(right, left.Type, true)) != null) 803 { 804 right = e; 805 } 806 else if ((e = PromoteExpression(left, right.Type, true)) != null) 807 { 808 left = e; 809 } 810 else 811 { 812 throw IncompatibleOperandsError(op.text, left, right, op.pos); 813 } 814 } 815 } 816 else 817 { 818 CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures), 819 op.text, ref left, ref right, op.pos); 820 } 821 switch (op.id) 822 { 823 case TokenId.Equal: 824 case TokenId.DoubleEqual: 825 left = GenerateEqual(left, right); 826 break; 827 case TokenId.ExclamationEqual: 828 case TokenId.LessGreater: 829 left = GenerateNotEqual(left, right); 830 break; 831 case TokenId.GreaterThan: 832 left = GenerateGreaterThan(left, right); 833 break; 834 case TokenId.GreaterThanEqual: 835 left = GenerateGreaterThanEqual(left, right); 836 break; 837 case TokenId.LessThan: 838 left = GenerateLessThan(left, right); 839 break; 840 case TokenId.LessThanEqual: 841 left = GenerateLessThanEqual(left, right); 842 break; 843 } 844 } 845 return left; 846 } 847 848 // +, -, & operators 849 Expression ParseAdditive() 850 { 851 Expression left = ParseMultiplicative(); 852 while (token.id == TokenId.Plus || token.id == TokenId.Minus || 853 token.id == TokenId.Amphersand) 854 { 855 Token op = token; 856 NextToken(); 857 Expression right = ParseMultiplicative(); 858 switch (op.id) 859 { 860 case TokenId.Plus: 861 if (left.Type == typeof(string) || right.Type == typeof(string)) 862 goto case TokenId.Amphersand; 863 CheckAndPromoteOperands(typeof(IAddSignatures), op.text, ref left, ref right, op.pos); 864 left = GenerateAdd(left, right); 865 break; 866 case TokenId.Minus: 867 CheckAndPromoteOperands(typeof(ISubtractSignatures), op.text, ref left, ref right, op.pos); 868 left = GenerateSubtract(left, right); 869 break; 870 case TokenId.Amphersand: 871 left = GenerateStringConcat(left, right); 872 break; 873 } 874 } 875 return left; 876 } 877 878 // *, /, %, mod operators 879 Expression ParseMultiplicative() 880 { 881 Expression left = ParseUnary(); 882 while (token.id == TokenId.Asterisk || token.id == TokenId.Slash || 883 token.id == TokenId.Percent || TokenIdentifierIs("mod")) 884 { 885 Token op = token; 886 NextToken(); 887 Expression right = ParseUnary(); 888 CheckAndPromoteOperands(typeof(IArithmeticSignatures), op.text, ref left, ref right, op.pos); 889 switch (op.id) 890 { 891 case TokenId.Asterisk: 892 left = Expression.Multiply(left, right); 893 break; 894 case TokenId.Slash: 895 left = Expression.Divide(left, right); 896 break; 897 case TokenId.Percent: 898 case TokenId.Identifier: 899 left = Expression.Modulo(left, right); 900 break; 901 } 902 } 903 return left; 904 } 905 906 // -, !, not unary operators 907 Expression ParseUnary() 908 { 909 if (token.id == TokenId.Minus || token.id == TokenId.Exclamation || 910 TokenIdentifierIs("not")) 911 { 912 Token op = token; 913 NextToken(); 914 if (op.id == TokenId.Minus && (token.id == TokenId.IntegerLiteral || 915 token.id == TokenId.RealLiteral)) 916 { 917 token.text = "-" + token.text; 918 token.pos = op.pos; 919 return ParsePrimary(); 920 } 921 Expression expr = ParseUnary(); 922 if (op.id == TokenId.Minus) 923 { 924 CheckAndPromoteOperand(typeof(INegationSignatures), op.text, ref expr, op.pos); 925 expr = Expression.Negate(expr); 926 } 927 else 928 { 929 CheckAndPromoteOperand(typeof(INotSignatures), op.text, ref expr, op.pos); 930 expr = Expression.Not(expr); 931 } 932 return expr; 933 } 934 return ParsePrimary(); 935 } 936 937 Expression ParsePrimary() 938 { 939 Expression expr = ParsePrimaryStart(); 940 while (true) 941 { 942 if (token.id == TokenId.Dot) 943 { 944 NextToken(); 945 expr = ParseMemberAccess(null, expr); 946 } 947 else if (token.id == TokenId.OpenBracket) 948 { 949 expr = ParseElementAccess(expr); 950 } 951 else 952 { 953 break; 954 } 955 } 956 return expr; 957 } 958 959 Expression ParsePrimaryStart() 960 { 961 switch (token.id) 962 { 963 case TokenId.Identifier: 964 return ParseIdentifier(); 965 case TokenId.StringLiteral: 966 return ParseStringLiteral(); 967 case TokenId.IntegerLiteral: 968 return ParseIntegerLiteral(); 969 case TokenId.RealLiteral: 970 return ParseRealLiteral(); 971 case TokenId.OpenParen: 972 return ParseParenExpression(); 973 default: 974 throw ParseError(Res.ExpressionExpected); 975 } 976 } 977 978 Expression ParseStringLiteral() 979 { 980 ValidateToken(TokenId.StringLiteral); 981 char quote = token.text[0]; 982 string s = token.text.Substring(1, token.text.Length - 2); 983 int start = 0; 984 while (true) 985 { 986 int i = s.IndexOf(quote, start); 987 if (i < 0) break; 988 s = s.Remove(i, 1); 989 start = i + 1; 990 } 991 if (quote == '\'') 992 { 993 if (s.Length != 1) 994 throw ParseError(Res.InvalidCharacterLiteral); 995 NextToken(); 996 return CreateLiteral(s[0], s); 997 } 998 NextToken(); 999 return CreateLiteral(s, s); 1000 } 1001 1002 Expression ParseIntegerLiteral() 1003 { 1004 ValidateToken(TokenId.IntegerLiteral); 1005 string text = token.text; 1006 if (text[0] != '-') 1007 { 1008 ulong value; 1009 if (!UInt64.TryParse(text, out value)) 1010 throw ParseError(Res.InvalidIntegerLiteral, text); 1011 NextToken(); 1012 if (value <= (ulong)Int32.MaxValue) return CreateLiteral((int)value, text); 1013 if (value <= (ulong)UInt32.MaxValue) return CreateLiteral((uint)value, text); 1014 if (value <= (ulong)Int64.MaxValue) return CreateLiteral((long)value, text); 1015 return CreateLiteral(value, text); 1016 } 1017 else 1018 { 1019 long value; 1020 if (!Int64.TryParse(text, out value)) 1021 throw ParseError(Res.InvalidIntegerLiteral, text); 1022 NextToken(); 1023 if (value >= Int32.MinValue && value <= Int32.MaxValue) 1024 return CreateLiteral((int)value, text); 1025 return CreateLiteral(value, text); 1026 } 1027 } 1028 1029 Expression ParseRealLiteral() 1030 { 1031 ValidateToken(TokenId.RealLiteral); 1032 string text = token.text; 1033 object value = null; 1034 char last = text[text.Length - 1]; 1035 if (last == 'F' || last == 'f') 1036 { 1037 float f; 1038 if (Single.TryParse(text.Substring(0, text.Length - 1), out f)) value = f; 1039 } 1040 else 1041 { 1042 double d; 1043 if (Double.TryParse(text, out d)) value = d; 1044 } 1045 if (value == null) throw ParseError(Res.InvalidRealLiteral, text); 1046 NextToken(); 1047 return CreateLiteral(value, text); 1048 } 1049 1050 Expression CreateLiteral(object value, string text) 1051 { 1052 ConstantExpression expr = Expression.Constant(value); 1053 literals.Add(expr, text); 1054 return expr; 1055 } 1056 1057 Expression ParseParenExpression() 1058 { 1059 ValidateToken(TokenId.OpenParen, Res.OpenParenExpected); 1060 NextToken(); 1061 Expression e = ParseExpression(); 1062 ValidateToken(TokenId.CloseParen, Res.CloseParenOrOperatorExpected); 1063 NextToken(); 1064 return e; 1065 } 1066 1067 Expression ParseIdentifier() 1068 { 1069 ValidateToken(TokenId.Identifier); 1070 object value; 1071 if (keywords.TryGetValue(token.text, out value)) 1072 { 1073 if (value is Type) return ParseTypeAccess((Type)value); 1074 if (value == (object)keywordIt) return ParseIt(); 1075 if (value == (object)keywordIif) return ParseIif(); 1076 if (value == (object)keywordNew) return ParseNew(); 1077 NextToken(); 1078 return (Expression)value; 1079 } 1080 if (symbols.TryGetValue(token.text, out value) || 1081 externals != null && externals.TryGetValue(token.text, out value)) 1082 { 1083 Expression expr = value as Expression; 1084 if (expr == null) 1085 { 1086 expr = Expression.Constant(value); 1087 } 1088 else 1089 { 1090 LambdaExpression lambda = expr as LambdaExpression; 1091 if (lambda != null) return ParseLambdaInvocation(lambda); 1092 } 1093 NextToken(); 1094 return expr; 1095 } 1096 if (it != null) return ParseMemberAccess(null, it); 1097 throw ParseError(Res.UnknownIdentifier, token.text); 1098 } 1099 1100 Expression ParseIt() 1101 { 1102 if (it == null) 1103 throw ParseError(Res.NoItInScope); 1104 NextToken(); 1105 return it; 1106 } 1107 1108 Expression ParseIif() 1109 { 1110 int errorPos = token.pos; 1111 NextToken(); 1112 Expression[] args = ParseArgumentList(); 1113 if (args.Length != 3) 1114 throw ParseError(errorPos, Res.IifRequiresThreeArgs); 1115 return GenerateConditional(args[0], args[1], args[2], errorPos); 1116 } 1117 1118 Expression GenerateConditional(Expression test, Expression expr1, Expression expr2, int errorPos) 1119 { 1120 if (test.Type != typeof(bool)) 1121 throw ParseError(errorPos, Res.FirstExprMustBeBool); 1122 if (expr1.Type != expr2.Type) 1123 { 1124 Expression expr1as2 = expr2 != nullLiteral ? PromoteExpression(expr1, expr2.Type, true) : null; 1125 Expression expr2as1 = expr1 != nullLiteral ? PromoteExpression(expr2, expr1.Type, true) : null; 1126 if (expr1as2 != null && expr2as1 == null) 1127 { 1128 expr1 = expr1as2; 1129 } 1130 else if (expr2as1 != null && expr1as2 == null) 1131 { 1132 expr2 = expr2as1; 1133 } 1134 else 1135 { 1136 string type1 = expr1 != nullLiteral ? expr1.Type.Name : "null"; 1137 string type2 = expr2 != nullLiteral ? expr2.Type.Name : "null"; 1138 if (expr1as2 != null && expr2as1 != null) 1139 throw ParseError(errorPos, Res.BothTypesConvertToOther, type1, type2); 1140 throw ParseError(errorPos, Res.NeitherTypeConvertsToOther, type1, type2); 1141 } 1142 } 1143 return Expression.Condition(test, expr1, expr2); 1144 } 1145 1146 Expression ParseNew() 1147 { 1148 NextToken(); 1149 ValidateToken(TokenId.OpenParen, Res.OpenParenExpected); 1150 NextToken(); 1151 List<DynamicProperty> properties = new List<DynamicProperty>(); 1152 List<Expression> expressions = new List<Expression>(); 1153 while (true) 1154 { 1155 int exprPos = token.pos; 1156 Expression expr = ParseExpression(); 1157 string propName; 1158 if (TokenIdentifierIs("as")) 1159 { 1160 NextToken(); 1161 propName = GetIdentifier(); 1162 NextToken(); 1163 } 1164 else 1165 { 1166 MemberExpression me = expr as MemberExpression; 1167 if (me == null) throw ParseError(exprPos, Res.MissingAsClause); 1168 propName = me.Member.Name; 1169 } 1170 expressions.Add(expr); 1171 properties.Add(new DynamicProperty(propName, expr.Type)); 1172 if (token.id != TokenId.Comma) break; 1173 NextToken(); 1174 } 1175 ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected); 1176 NextToken(); 1177 Type type = DynamicExpression.CreateClass(properties); 1178 MemberBinding[] bindings = new MemberBinding[properties.Count]; 1179 for (int i = 0; i < bindings.Length; i++) 1180 bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]); 1181 return Expression.MemberInit(Expression.New(type), bindings); 1182 } 1183 1184 Expression ParseLambdaInvocation(LambdaExpression lambda) 1185 { 1186 int errorPos = token.pos; 1187 NextToken(); 1188 Expression[] args = ParseArgumentList(); 1189 MethodBase method; 1190 if (FindMethod(lambda.Type, "Invoke", false, args, out method) != 1) 1191 throw ParseError(errorPos, Res.ArgsIncompatibleWithLambda); 1192 return Expression.Invoke(lambda, args); 1193 } 1194 1195 Expression ParseTypeAccess(Type type) 1196 { 1197 int errorPos = token.pos; 1198 NextToken(); 1199 if (token.id == TokenId.Question) 1200 { 1201 if (!type.IsValueType || IsNullableType(type)) 1202 throw ParseError(errorPos, Res.TypeHasNoNullableForm, GetTypeName(type)); 1203 type = typeof(Nullable<>).MakeGenericType(type); 1204 NextToken(); 1205 } 1206 if (token.id == TokenId.OpenParen) 1207 { 1208 Expression[] args = ParseArgumentList(); 1209 MethodBase method; 1210 switch (FindBestMethod(type.GetConstructors(), args, out method)) 1211 { 1212 case 0: 1213 if (args.Length == 1) 1214 return GenerateConversion(args[0], type, errorPos); 1215 throw ParseError(errorPos, Res.NoMatchingConstructor, GetTypeName(type)); 1216 case 1: 1217 return Expression.New((ConstructorInfo)method, args); 1218 default: 1219 throw ParseError(errorPos, Res.AmbiguousConstructorInvocation, GetTypeName(type)); 1220 } 1221 } 1222 ValidateToken(TokenId.Dot, Res.DotOrOpenParenExpected); 1223 NextToken(); 1224 return ParseMemberAccess(type, null); 1225 } 1226 1227 Expression GenerateConversion(Expression expr, Type type, int errorPos) 1228 { 1229 Type exprType = expr.Type; 1230 if (exprType == type) return expr; 1231 if (exprType.IsValueType && type.IsValueType) 1232 { 1233 if ((IsNullableType(exprType) || IsNullableType(type)) && 1234 GetNonNullableType(exprType) == GetNonNullableType(type)) 1235 return Expression.Convert(expr, type); 1236 if ((IsNumericType(exprType) || IsEnumType(exprType)) && 1237 (IsNumericType(type)) || IsEnumType(type)) 1238 return Expression.ConvertChecked(expr, type); 1239 } 1240 if (exprType.IsAssignableFrom(type) || type.IsAssignableFrom(exprType) || 1241 exprType.IsInterface || type.IsInterface) 1242 return Expression.Convert(expr, type); 1243 throw ParseError(errorPos, Res.CannotConvertValue, 1244 GetTypeName(exprType), GetTypeName(type)); 1245 } 1246 1247 Expression ParseMemberAccess(Type type, Expression instance) 1248 { 1249 if (instance != null) type = instance.Type; 1250 int errorPos = token.pos; 1251 string id = GetIdentifier(); 1252 NextToken(); 1253 if (token.id == TokenId.OpenParen) 1254 { 1255 if (instance != null && type != typeof(string)) 1256 { 1257 Type enumerableType = FindGenericType(typeof(IEnumerable<>), type); 1258 if (enumerableType != null) 1259 { 1260 Type elementType = enumerableType.GetGenericArguments()[0]; 1261 return ParseAggregate(instance, elementType, id, errorPos); 1262 } 1263 } 1264 Expression[] args = ParseArgumentList(); 1265 MethodBase mb; 1266 switch (FindMethod(type, id, instance == null, args, out mb)) 1267 { 1268 case 0: 1269 throw ParseError(errorPos, Res.NoApplicableMethod, 1270 id, GetTypeName(type)); 1271 case 1: 1272 MethodInfo method = (MethodInfo)mb; 1273 if (!IsPredefinedType(method.DeclaringType)) 1274 throw ParseError(errorPos, Res.MethodsAreInaccessible, GetTypeName(method.DeclaringType)); 1275 if (method.ReturnType == typeof(void)) 1276 throw ParseError(errorPos, Res.MethodIsVoid, 1277 id, GetTypeName(method.DeclaringType)); 1278 return Expression.Call(instance, (MethodInfo)method, args); 1279 default: 1280 throw ParseError(errorPos, Res.AmbiguousMethodInvocation, 1281 id, GetTypeName(type)); 1282 } 1283 } 1284 else 1285 { 1286 MemberInfo member = FindPropertyOrField(type, id, instance == null); 1287 if (member == null) 1288 throw ParseError(errorPos, Res.UnknownPropertyOrField, 1289 id, GetTypeName(type)); 1290 return member is PropertyInfo ? 1291 Expression.Property(instance, (PropertyInfo)member) : 1292 Expression.Field(instance, (FieldInfo)member); 1293 } 1294 } 1295 1296 static Type FindGenericType(Type generic, Type type) 1297 { 1298 while (type != null && type != typeof(object)) 1299 { 1300 if (type.IsGenericType && type.GetGenericTypeDefinition() == generic) return type; 1301 if (generic.IsInterface) 1302 { 1303 foreach (Type intfType in type.GetInterfaces()) 1304 { 1305 Type found = FindGenericType(generic, intfType); 1306 if (found != null) return found; 1307 } 1308 } 1309 type = type.BaseType; 1310 } 1311 return null; 1312 } 1313 1314 Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos) 1315 { 1316 ParameterExpression outerIt = it; 1317 ParameterExpression innerIt = Expression.Parameter(elementType, ""); 1318 it = innerIt; 1319 Expression[] args = ParseArgumentList(); 1320 it = outerIt; 1321 MethodBase signature; 1322 if (FindMethod(typeof(IEnumerableSignatures), methodName, false, args, out signature) != 1) 1323 throw ParseError(errorPos, Res.NoApplicableAggregate, methodName); 1324 Type[] typeArgs; 1325 if (signature.Name == "Min" || signature.Name == "Max") 1326 { 1327 typeArgs = new Type[] { elementType, args[0].Type }; 1328 } 1329 else 1330 { 1331 typeArgs = new Type[] { elementType }; 1332 } 1333 if (args.Length == 0) 1334 { 1335 args = new Expression[] { instance }; 1336 } 1337 else 1338 { 1339 args = new Expression[] { instance, Expression.Lambda(args[0], innerIt) }; 1340 } 1341 return Expression.Call(typeof(Enumerable), signature.Name, typeArgs, args); 1342 } 1343 1344 Expression[] ParseArgumentList() 1345 { 1346 ValidateToken(TokenId.OpenParen, Res.OpenParenExpected); 1347 NextToken(); 1348 Expression[] args = token.id != TokenId.CloseParen ? ParseArguments() : new Expression[0]; 1349 ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected); 1350 NextToken(); 1351 return args; 1352 } 1353 1354 Expression[] ParseArguments() 1355 { 1356 List<Expression> argList = new List<Expression>(); 1357 while (true) 1358 { 1359 argList.Add(ParseExpression()); 1360 if (token.id != TokenId.Comma) break; 1361 NextToken(); 1362 } 1363 return argList.ToArray(); 1364 } 1365 1366 Expression ParseElementAccess(Expression expr) 1367 { 1368 int errorPos = token.pos; 1369 ValidateToken(TokenId.OpenBracket, Res.OpenParenExpected); 1370 NextToken(); 1371 Expression[] args = ParseArguments(); 1372 ValidateToken(TokenId.CloseBracket, Res.CloseBracketOrCommaExpected); 1373 NextToken(); 1374 if (expr.Type.IsArray) 1375 { 1376 if (expr.Type.GetArrayRank() != 1 || args.Length != 1) 1377 throw ParseError(errorPos, Res.CannotIndexMultiDimArray); 1378 Expression index = PromoteExpression(args[0], typeof(int), true); 1379 if (index == null) 1380 throw ParseError(errorPos, Res.InvalidIndex); 1381 return Expression.ArrayIndex(expr, index); 1382 } 1383 else 1384 { 1385 MethodBase mb; 1386 switch (FindIndexer(expr.Type, args, out mb)) 1387 { 1388 case 0: 1389 throw ParseError(errorPos, Res.NoApplicableIndexer, 1390 GetTypeName(expr.Type)); 1391 case 1: 1392 return Expression.Call(expr, (MethodInfo)mb, args); 1393 default: 1394 throw ParseError(errorPos, Res.AmbiguousIndexerInvocation, 1395 GetTypeName(expr.Type)); 1396 } 1397 } 1398 } 1399 1400 static bool IsPredefinedType(Type type) 1401 { 1402 foreach (Type t in predefinedTypes) if (t == type) return true; 1403 return false; 1404 } 1405 1406 static bool IsNullableType(Type type) 1407 { 1408 return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); 1409 } 1410 1411 static Type GetNonNullableType(Type type) 1412 { 1413 return IsNullableType(type) ? type.GetGenericArguments()[0] : type; 1414 } 1415 1416 static string GetTypeName(Type type) 1417 { 1418 Type baseType = GetNonNullableType(type); 1419 string s = baseType.Name; 1420 if (type != baseType) s += '?'; 1421 return s; 1422 } 1423 1424 static bool IsNumericType(Type type) 1425 { 1426 return GetNumericTypeKind(type) != 0; 1427 } 1428 1429 static bool IsSignedIntegralType(Type type) 1430 { 1431 return GetNumericTypeKind(type) == 2; 1432 } 1433 1434 static bool IsUnsignedIntegralType(Type type) 1435 { 1436 return GetNumericTypeKind(type) == 3; 1437 } 1438 1439 static int GetNumericTypeKind(Type type) 1440 { 1441 type = GetNonNullableType(type); 1442 if (type.IsEnum) return 0; 1443 switch (Type.GetTypeCode(type)) 1444 { 1445 case TypeCode.Char: 1446 case TypeCode.Single: 1447 case TypeCode.Double: 1448 case TypeCode.Decimal: 1449 return 1; 1450 case TypeCode.SByte: 1451 case TypeCode.Int16: 1452 case TypeCode.Int32: 1453 case TypeCode.Int64: 1454 return 2; 1455 case TypeCode.Byte: 1456 case TypeCode.UInt16: 1457 case TypeCode.UInt32: 1458 case TypeCode.UInt64: 1459 return 3; 1460 default: 1461 return 0; 1462 } 1463 } 1464 1465 static bool IsEnumType(Type type) 1466 { 1467 return GetNonNullableType(type).IsEnum; 1468 } 1469 1470 void CheckAndPromoteOperand(Type signatures, string opName, ref Expression expr, int errorPos) 1471 { 1472 Expression[] args = new Expression[] { expr }; 1473 MethodBase method; 1474 if (FindMethod(signatures, "F", false, args, out method) != 1) 1475 throw ParseError(errorPos, Res.IncompatibleOperand, 1476 opName, GetTypeName(args[0].Type)); 1477 expr = args[0]; 1478 } 1479 1480 void CheckAndPromoteOperands(Type signatures, string opName, ref Expression left, ref Expression right, int errorPos) 1481 { 1482 Expression[] args = new Expression[] { left, right }; 1483 MethodBase method; 1484 if (FindMethod(signatures, "F", false, args, out method) != 1) 1485 throw IncompatibleOperandsError(opName, left, right, errorPos); 1486 left = args[0]; 1487 right = args[1]; 1488 } 1489 1490 Exception IncompatibleOperandsError(string opName, Expression left, Expression right, int pos) 1491 { 1492 return ParseError(pos, Res.IncompatibleOperands, 1493 opName, GetTypeName(left.Type), GetTypeName(right.Type)); 1494 } 1495 1496 MemberInfo FindPropertyOrField(Type type, string memberName, bool staticAccess) 1497 { 1498 BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | 1499 (staticAccess ? BindingFlags.Static : BindingFlags.Instance); 1500 foreach (Type t in SelfAndBaseTypes(type)) 1501 { 1502 MemberInfo[] members = t.FindMembers(MemberTypes.Property | MemberTypes.Field, 1503 flags, Type.FilterNameIgnoreCase, memberName); 1504 if (members.Length != 0) return members[0]; 1505 } 1506 return null; 1507 } 1508 1509 int FindMethod(Type type, string methodName, bool staticAccess, Expression[] args, out MethodBase method) 1510 { 1511 BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | 1512 (staticAccess ? BindingFlags.Static : BindingFlags.Instance); 1513 foreach (Type t in SelfAndBaseTypes(type)) 1514 { 1515 MemberInfo[] members = t.FindMembers(MemberTypes.Method, 1516 flags, Type.FilterNameIgnoreCase, methodName); 1517 int count = FindBestMethod(members.Cast<MethodBase>(), args, out method); 1518 if (count != 0) return count; 1519 } 1520 method = null; 1521 return 0; 1522 } 1523 1524 int FindIndexer(Type type, Expression[] args, out MethodBase method) 1525 { 1526 foreach (Type t in SelfAndBaseTypes(type)) 1527 { 1528 MemberInfo[] members = t.GetDefaultMembers(); 1529 if (members.Length != 0) 1530 { 1531 IEnumerable<MethodBase> methods = members. 1532 OfType<PropertyInfo>(). 1533 Select(p => (MethodBase)p.GetGetMethod()). 1534 Where(m => m != null); 1535 int count = FindBestMethod(methods, args, out method); 1536 if (count != 0) return count; 1537 } 1538 } 1539 method = null; 1540 return 0; 1541 } 1542 1543 static IEnumerable<Type> SelfAndBaseTypes(Type type) 1544 { 1545 if (type.IsInterface) 1546 { 1547 List<Type> types = new List<Type>(); 1548 AddInterface(types, type); 1549 return types; 1550 } 1551 return SelfAndBaseClasses(type); 1552 } 1553 1554 static IEnumerable<Type> SelfAndBaseClasses(Type type) 1555 { 1556 while (type != null) 1557 { 1558 yield return type; 1559 type = type.BaseType; 1560 } 1561 } 1562 1563 static void AddInterface(List<Type> types, Type type) 1564 { 1565 if (!types.Contains(type)) 1566 { 1567 types.Add(type); 1568 foreach (Type t in type.GetInterfaces()) AddInterface(types, t); 1569 } 1570 } 1571 1572 class MethodData 1573 { 1574 public MethodBase MethodBase; 1575 public ParameterInfo[] Parameters; 1576 public Expression[] Args; 1577 } 1578 1579 int FindBestMethod(IEnumerable<MethodBase> methods, Expression[] args, out MethodBase method) 1580 { 1581 MethodData[] applicable = methods. 1582 Select(m => new MethodData { MethodBase = m, Parameters = m.GetParameters() }). 1583 Where(m => IsApplicable(m, args)). 1584 ToArray(); 1585 if (applicable.Length > 1) 1586 { 1587 applicable = applicable. 1588 Where(m => applicable.All(n => m == n || IsBetterThan(args, m, n))). 1589 ToArray(); 1590 } 1591 if (applicable.Length == 1) 1592 { 1593 MethodData md = applicable[0]; 1594 for (int i = 0; i < args.Length; i++) args[i] = md.Args[i]; 1595 method = md.MethodBase; 1596 } 1597 else 1598 { 1599 method = null; 1600 } 1601 return applicable.Length; 1602 } 1603 1604 bool IsApplicable(MethodData method, Expression[] args) 1605 { 1606 if (method.Parameters.Length != args.Length) return false; 1607 Expression[] promotedArgs = new Expression[args.Length]; 1608 for (int i = 0; i < args.Length; i++) 1609 { 1610 ParameterInfo pi = method.Parameters[i]; 1611 if (pi.IsOut) return false; 1612 Expression promoted = PromoteExpression(args[i], pi.ParameterType, false); 1613 if (promoted == null) return false; 1614 promotedArgs[i] = promoted; 1615 } 1616 method.Args = promotedArgs; 1617 return true; 1618 } 1619 1620 Expression PromoteExpression(Expression expr, Type type, bool exact) 1621 { 1622 if (expr.Type == type) return expr; 1623 if (expr is ConstantExpression) 1624 { 1625 ConstantExpression ce = (ConstantExpression)expr; 1626 if (ce == nullLiteral) 1627 { 1628 if (!type.IsValueType || IsNullableType(type)) 1629 return Expression.Constant(null, type); 1630 } 1631 else 1632 { 1633 string text; 1634 if (literals.TryGetValue(ce, out text)) 1635 { 1636 Type target = GetNonNullableType(type); 1637 Object value = null; 1638 switch (Type.GetTypeCode(ce.Type)) 1639 { 1640 case TypeCode.Int32: 1641 case TypeCode.UInt32: 1642 case TypeCode.Int64: 1643 case TypeCode.UInt64: 1644 value = ParseNumber(text, target); 1645 break; 1646 case TypeCode.Double: 1647 if (target == typeof(decimal)) value = ParseNumber(text, target); 1648 break; 1649 case TypeCode.String: 1650 value = ParseEnum(text, target); 1651 break; 1652 } 1653 if (value != null) 1654 return Expression.Constant(value, type); 1655 } 1656 } 1657 } 1658 if (IsCompatibleWith(expr.Type, type)) 1659 { 1660 if (type.IsValueType || exact) return Expression.Convert(expr, type); 1661 return expr; 1662 } 1663 return null; 1664 } 1665 1666 static object ParseNumber(string text, Type type) 1667 { 1668 switch (Type.GetTypeCode(GetNonNullableType(type))) 1669 { 1670 case TypeCode.SByte: 1671 sbyte sb; 1672 if (sbyte.TryParse(text, out sb)) return sb; 1673 break; 1674 case TypeCode.Byte: 1675 byte b; 1676 if (byte.TryParse(text, out b)) return b; 1677 break; 1678 case TypeCode.Int16: 1679 short s; 1680 if (short.TryParse(text, out s)) return s; 1681 break; 1682 case TypeCode.UInt16: 1683 ushort us; 1684 if (ushort.TryParse(text, out us)) return us; 1685 break; 1686 case TypeCode.Int32: 1687 int i; 1688 if (int.TryParse(text, out i)) return i; 1689 break; 1690 case TypeCode.UInt32: 1691 uint ui; 1692 if (uint.TryParse(text, out ui)) return ui; 1693 break; 1694 case TypeCode.Int64: 1695 long l; 1696 if (long.TryParse(text, out l)) return l; 1697 break; 1698 case TypeCode.UInt64: 1699 ulong ul; 1700 if (ulong.TryParse(text, out ul)) return ul; 1701 break; 1702 case TypeCode.Single: 1703 float f; 1704 if (float.TryParse(text, out f)) return f; 1705 break; 1706 case TypeCode.Double: 1707 double d; 1708 if (double.TryParse(text, out d)) return d; 1709 break; 1710 case TypeCode.Decimal: 1711 decimal e; 1712 if (decimal.TryParse(text, out e)) return e; 1713 break; 1714 } 1715 return null; 1716 } 1717 1718 static object ParseEnum(string name, Type type) 1719 { 1720 if (type.IsEnum) 1721 { 1722 MemberInfo[] memberInfos = type.FindMembers(MemberTypes.Field, 1723 BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static, 1724 Type.FilterNameIgnoreCase, name); 1725 if (memberInfos.Length != 0) return ((FieldInfo)memberInfos[0]).GetValue(null); 1726 } 1727 return null; 1728 } 1729 1730 static bool IsCompatibleWith(Type source, Type target) 1731 { 1732 if (source == target) return true; 1733 if (!target.IsValueType) return target.IsAssignableFrom(source); 1734 Type st = GetNonNullableType(source); 1735 Type tt = GetNonNullableType(target); 1736 if (st != source && tt == target) return false; 1737 TypeCode sc = st.IsEnum ? TypeCode.Object : Type.GetTypeCode(st); 1738 TypeCode tc = tt.IsEnum ? TypeCode.Object : Type.GetTypeCode(tt); 1739 switch (sc) 1740 { 1741 case TypeCode.SByte: 1742 switch (tc) 1743 { 1744 case TypeCode.SByte: 1745 case TypeCode.Int16: 1746 case TypeCode.Int32: 1747 case TypeCode.Int64: 1748 case TypeCode.Single: 1749 case TypeCode.Double: 1750 case TypeCode.Decimal: 1751 return true; 1752 } 1753 break; 1754 case TypeCode.Byte: 1755 switch (tc) 1756 { 1757 case TypeCode.Byte: 1758 case TypeCode.Int16: 1759 case TypeCode.UInt16: 1760 case TypeCode.Int32: 1761 case TypeCode.UInt32: 1762 case TypeCode.Int64: 1763 case TypeCode.UInt64: 1764 case TypeCode.Single: 1765 case TypeCode.Double: 1766 case TypeCode.Decimal: 1767 return true; 1768 } 1769 break; 1770 case TypeCode.Int16: 1771 switch (tc) 1772 { 1773 case TypeCode.Int16: 1774 case TypeCode.Int32: 1775 case TypeCode.Int64: 1776 case TypeCode.Single: 1777 case TypeCode.Double: 1778 case TypeCode.Decimal: 1779 return true; 1780 } 1781 break; 1782 case TypeCode.UInt16: 1783 switch (tc) 1784 { 1785 case TypeCode.UInt16: 1786 case TypeCode.Int32: 1787 case TypeCode.UInt32: 1788 case TypeCode.Int64: 1789 case TypeCode.UInt64: 1790 case TypeCode.Single: 1791 case TypeCode.Double: 1792 case TypeCode.Decimal: 1793 return true; 1794 } 1795 break; 1796 case TypeCode.Int32: 1797 switch (tc) 1798 { 1799 case TypeCode.Int32: 1800 case TypeCode.Int64: 1801 case TypeCode.Single: 1802 case TypeCode.Double: 1803 case TypeCode.Decimal: 1804 return true; 1805 } 1806 break; 1807 case TypeCode.UInt32: 1808 switch (tc) 1809 { 1810 case TypeCode.UInt32: 1811 case TypeCode.Int64: 1812 case TypeCode.UInt64: 1813 case TypeCode.Single: 1814 case TypeCode.Double: 1815 case TypeCode.Decimal: 1816 return true; 1817 } 1818 break; 1819 case TypeCode.Int64: 1820 switch (tc) 1821 { 1822 case TypeCode.Int64: 1823 case TypeCode.Single: 1824 case TypeCode.Double: 1825 case TypeCode.Decimal: 1826 return true; 1827 } 1828 break; 1829 case TypeCode.UInt64: 1830 switch (tc) 1831 { 1832 case TypeCode.UInt64: 1833 case TypeCode.Single: 1834 case TypeCode.Double: 1835 case TypeCode.Decimal: 1836 return true; 1837 } 1838 break; 1839 case TypeCode.Single: 1840 switch (tc) 1841 { 1842 case TypeCode.Single: 1843 case TypeCode.Double: 1844 return true; 1845 } 1846 break; 1847 default: 1848 if (st == tt) return true; 1849 break; 1850 } 1851 return false; 1852 } 1853 1854 static bool IsBetterThan(Expression[] args, MethodData m1, MethodData m2) 1855 { 1856 bool better = false; 1857 for (int i = 0; i < args.Length; i++) 1858 { 1859 int c = CompareConversions(args[i].Type, 1860 m1.Parameters[i].ParameterType, 1861 m2.Parameters[i].ParameterType); 1862 if (c < 0) return false; 1863 if (c > 0) better = true; 1864 } 1865 return better; 1866 } 1867 1868 // Return 1 if s -> t1 is a better conversion than s -> t2 1869 // Return -1 if s -> t2 is a better conversion than s -> t1 1870 // Return 0 if neither conversion is better 1871 static int CompareConversions(Type s, Type t1, Type t2) 1872 { 1873 if (t1 == t2) return 0; 1874 if (s == t1) return 1; 1875 if (s == t2) return -1; 1876 bool t1t2 = IsCompatibleWith(t1, t2); 1877 bool t2t1 = IsCompatibleWith(t2, t1); 1878 if (t1t2 && !t2t1) return 1; 1879 if (t2t1 && !t1t2) return -1; 1880 if (IsSignedIntegralType(t1) && IsUnsignedIntegralType(t2)) return 1; 1881 if (IsSignedIntegralType(t2) && IsUnsignedIntegralType(t1)) return -1; 1882 return 0; 1883 } 1884 1885 Expression GenerateEqual(Expression left, Expression right) 1886 { 1887 return Expression.Equal(left, right); 1888 } 1889 1890 Expression GenerateNotEqual(Expression left, Expression right) 1891 { 1892 return Expression.NotEqual(left, right); 1893 } 1894 1895 Expression GenerateGreaterThan(Expression left, Expression right) 1896 { 1897 if (left.Type == typeof(string)) 1898 { 1899 return Expression.GreaterThan( 1900 GenerateStaticMethodCall("Compare", left, right), 1901 Expression.Constant(0) 1902 ); 1903 } 1904 return Expression.GreaterThan(left, right); 1905 } 1906 1907 Expression GenerateGreaterThanEqual(Expression left, Expression right) 1908 { 1909 if (left.Type == typeof(string)) 1910 { 1911 return Expression.GreaterThanOrEqual( 1912 GenerateStaticMethodCall("Compare", left, right), 1913 Expression.Constant(0) 1914 ); 1915 } 1916 return Expression.GreaterThanOrEqual(left, right); 1917 } 1918 1919 Expression GenerateLessThan(Expression left, Expression right) 1920 { 1921 if (left.Type == typeof(string)) 1922 { 1923 return Expression.LessThan( 1924 GenerateStaticMethodCall("Compare", left, right), 1925 Expression.Constant(0) 1926 ); 1927 } 1928 return Expression.LessThan(left, right); 1929 } 1930 1931 Expression GenerateLessThanEqual(Expression left, Expression right) 1932 { 1933 if (left.Type == typeof(string)) 1934 { 1935 return Expression.LessThanOrEqual( 1936 GenerateStaticMethodCall("Compare", left, right), 1937 Expression.Constant(0) 1938 ); 1939 } 1940 return Expression.LessThanOrEqual(left, right); 1941 } 1942 1943 Expression GenerateAdd(Expression left, Expression right) 1944 { 1945 if (left.Type == typeof(string) && right.Type == typeof(string)) 1946 { 1947 return GenerateStaticMethodCall("Concat", left, right); 1948 } 1949 return Expression.Add(left, right); 1950 } 1951 1952 Expression GenerateSubtract(Expression left, Expression right) 1953 { 1954 return Expression.Subtract(left, right); 1955 } 1956 1957 Expression GenerateStringConcat(Expression left, Expression right) 1958 { 1959 return Expression.Call( 1960 null, 1961 typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) }), 1962 new[] { left, right }); 1963 } 1964 1965 MethodInfo GetStaticMethod(string methodName, Expression left, Expression right) 1966 { 1967 return left.Type.GetMethod(methodName, new[] { left.Type, right.Type }); 1968 } 1969 1970 Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right) 1971 { 1972 return Expression.Call(null, GetStaticMethod(methodName, left, right), new[] { left, right }); 1973 } 1974 1975 void SetTextPos(int pos) 1976 { 1977 textPos = pos; 1978 ch = textPos < textLen ? text[textPos] : '\0'; 1979 } 1980 1981 void NextChar() 1982 { 1983 if (textPos < textLen) textPos++; 1984 ch = textPos < textLen ? text[textPos] : '\0'; 1985 } 1986 1987 void NextToken() 1988 { 1989 while (Char.IsWhiteSpace(ch)) NextChar(); 1990 TokenId t; 1991 int tokenPos = textPos; 1992 switch (ch) 1993 { 1994 case '!': 1995 NextChar(); 1996 if (ch == '=') 1997 { 1998 NextChar(); 1999 t = TokenId.ExclamationEqual; 2000 } 2001 else 2002 { 2003 t = TokenId.Exclamation; 2004 } 2005 break; 2006 case '%': 2007 NextChar(); 2008 t = TokenId.Percent; 2009 break; 2010 case '&': 2011 NextChar(); 2012 if (ch == '&') 2013 { 2014 NextChar(); 2015 t = TokenId.DoubleAmphersand; 2016 } 2017 else 2018 { 2019 t = TokenId.Amphersand; 2020 } 2021 break; 2022 case '(': 2023 NextChar(); 2024 t = TokenId.OpenParen; 2025 break; 2026 case ')': 2027 NextChar(); 2028 t = TokenId.CloseParen; 2029 break; 2030 case '*': 2031 NextChar(); 2032 t = TokenId.Asterisk; 2033 break; 2034 case '+': 2035 NextChar(); 2036 t = TokenId.Plus; 2037 break; 2038 case ',': 2039 NextChar(); 2040 t = TokenId.Comma; 2041 break; 2042 case '-': 2043 NextChar(); 2044 t = TokenId.Minus; 2045 break; 2046 case '.': 2047 NextChar(); 2048 t = TokenId.Dot; 2049 break; 2050 case '/': 2051 NextChar(); 2052 t = TokenId.Slash; 2053 break; 2054 case ':': 2055 NextChar(); 2056 t = TokenId.Colon; 2057 break; 2058 case '<': 2059 NextChar(); 2060 if (ch == '=') 2061 { 2062 NextChar(); 2063 t = TokenId.LessThanEqual; 2064 } 2065 else if (ch == '>') 2066 { 2067 NextChar(); 2068 t = TokenId.LessGreater; 2069 } 2070 else 2071 { 2072 t = TokenId.LessThan; 2073 } 2074 break; 2075 case '=': 2076 NextChar(); 2077 if (ch == '=') 2078 { 2079 NextChar(); 2080 t = TokenId.DoubleEqual; 2081 } 2082 else 2083 { 2084 t = TokenId.Equal; 2085 } 2086 break; 2087 case '>': 2088 NextChar(); 2089 if (ch == '=') 2090 { 2091 NextChar(); 2092 t = TokenId.GreaterThanEqual; 2093 } 2094 else 2095 { 2096 t = TokenId.GreaterThan; 2097 } 2098 break; 2099 case '?': 2100 NextChar(); 2101 t = TokenId.Question; 2102 break; 2103 case '[': 2104 NextChar(); 2105 t = TokenId.OpenBracket; 2106 break; 2107 case ']': 2108 NextChar(); 2109 t = TokenId.CloseBracket; 2110 break; 2111 case '|': 2112 NextChar(); 2113 if (ch == '|') 2114 { 2115 NextChar(); 2116 t = TokenId.DoubleBar; 2117 } 2118 else 2119 { 2120 t = TokenId.Bar; 2121 } 2122 break; 2123 case '"': 2124 case '\'': 2125 char quote = ch; 2126 do 2127 { 2128 NextChar(); 2129 while (textPos < textLen && ch != quote) NextChar(); 2130 if (textPos == textLen) 2131 throw ParseError(textPos, Res.UnterminatedStringLiteral); 2132 NextChar(); 2133 } while (ch == quote); 2134 t = TokenId.StringLiteral; 2135 break; 2136 default: 2137 if (Char.IsLetter(ch) || ch == '@' || ch == '_') 2138 { 2139 do 2140 { 2141 NextChar(); 2142 } while (Char.IsLetterOrDigit(ch) || ch == '_'); 2143 t = TokenId.Identifier; 2144 break; 2145 } 2146 if (Char.IsDigit(ch)) 2147 { 2148 t = TokenId.IntegerLiteral; 2149 do 2150 { 2151 NextChar(); 2152 } while (Char.IsDigit(ch)); 2153 if (ch == '.') 2154 { 2155 t = TokenId.RealLiteral; 2156 NextChar(); 2157 ValidateDigit(); 2158 do 2159 { 2160 NextChar(); 2161 } while (Char.IsDigit(ch)); 2162 } 2163 if (ch == 'E' || ch == 'e') 2164 { 2165 t = TokenId.RealLiteral; 2166 NextChar(); 2167 if (ch == '+' || ch == '-') NextChar(); 2168 ValidateDigit(); 2169 do 2170 { 2171 NextChar(); 2172 } while (Char.IsDigit(ch)); 2173 } 2174 if (ch == 'F' || ch == 'f') NextChar(); 2175 break; 2176 } 2177 if (textPos == textLen) 2178 { 2179 t = TokenId.End; 2180 break; 2181 } 2182 throw ParseError(textPos, Res.InvalidCharacter, ch); 2183 } 2184 token.id = t; 2185 token.text = text.Substring(tokenPos, textPos - tokenPos); 2186 token.pos = tokenPos; 2187 } 2188 2189 bool TokenIdentifierIs(string id) 2190 { 2191 return token.id == TokenId.Identifier && String.Equals(id, token.text, StringComparison.OrdinalIgnoreCase); 2192 } 2193 2194 string GetIdentifier() 2195 { 2196 ValidateToken(TokenId.Identifier, Res.IdentifierExpected); 2197 string id = token.text; 2198 if (id.Length > 1 && id[0] == '@') id = id.Substring(1); 2199 return id; 2200 } 2201 2202 void ValidateDigit() 2203 { 2204 if (!Char.IsDigit(ch)) throw ParseError(textPos, Res.DigitExpected); 2205 } 2206 2207 void ValidateToken(TokenId t, string errorMessage) 2208 { 2209 if (token.id != t) throw ParseError(errorMessage); 2210 } 2211 2212 void ValidateToken(TokenId t) 2213 { 2214 if (token.id != t) throw ParseError(Res.SyntaxError); 2215 } 2216 2217 Exception ParseError(string format, params object[] args) 2218 { 2219 return ParseError(token.pos, format, args); 2220 } 2221 2222 Exception ParseError(int pos, string format, params object[] args) 2223 { 2224 return new ParseException(string.Format(System.Globalization.CultureInfo.CurrentCulture, format, args), pos); 2225 } 2226 2227 static Dictionary<string, object> CreateKeywords() 2228 { 2229 Dictionary<string, object> d = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); 2230 d.Add("true", trueLiteral); 2231 d.Add("false", falseLiteral); 2232 d.Add("null", nullLiteral); 2233 d.Add(keywordIt, keywordIt); 2234 d.Add(keywordIif, keywordIif); 2235 d.Add(keywordNew, keywordNew); 2236 foreach (Type type in predefinedTypes) d.Add(type.Name, type); 2237 return d; 2238 } 2239 } 2240 2241 static class Res 2242 { 2243 public const string DuplicateIdentifier = "The identifier '{0}' was defined more than once"; 2244 public const string ExpressionTypeMismatch = "Expression of type '{0}' expected"; 2245 public const string ExpressionExpected = "Expression expected"; 2246 public const string InvalidCharacterLiteral = "Character literal must contain exactly one character"; 2247 public const string InvalidIntegerLiteral = "Invalid integer literal '{0}'"; 2248 public const string InvalidRealLiteral = "Invalid real literal '{0}'"; 2249 public const string UnknownIdentifier = "Unknown identifier '{0}'"; 2250 public const string NoItInScope = "No 'it' is in scope"; 2251 public const string IifRequiresThreeArgs = "The 'iif' function requires three arguments"; 2252 public const string FirstExprMustBeBool = "The first expression must be of type 'Boolean'"; 2253 public const string BothTypesConvertToOther = "Both of the types '{0}' and '{1}' convert to the other"; 2254 public const string NeitherTypeConvertsToOther = "Neither of the types '{0}' and '{1}' converts to the other"; 2255 public const string MissingAsClause = "Expression is missing an 'as' clause"; 2256 public const string ArgsIncompatibleWithLambda = "Argument list incompatible with lambda expression"; 2257 public const string TypeHasNoNullableForm = "Type '{0}' has no nullable form"; 2258 public const string NoMatchingConstructor = "No matching constructor in type '{0}'"; 2259 public const string AmbiguousConstructorInvocation = "Ambiguous invocation of '{0}' constructor"; 2260 public const string CannotConvertValue = "A value of type '{0}' cannot be converted to type '{1}'"; 2261 public const string NoApplicableMethod = "No applicable method '{0}' exists in type '{1}'"; 2262 public const string MethodsAreInaccessible = "Methods on type '{0}' are not accessible"; 2263 public const string MethodIsVoid = "Method '{0}' in type '{1}' does not return a value"; 2264 public const string AmbiguousMethodInvocation = "Ambiguous invocation of method '{0}' in type '{1}'"; 2265 public const string UnknownPropertyOrField = "No property or field '{0}' exists in type '{1}'"; 2266 public const string NoApplicableAggregate = "No applicable aggregate method '{0}' exists"; 2267 public const string CannotIndexMultiDimArray = "Indexing of multi-dimensional arrays is not supported"; 2268 public const string InvalidIndex = "Array index must be an integer expression"; 2269 public const string NoApplicableIndexer = "No applicable indexer exists in type '{0}'"; 2270 public const string AmbiguousIndexerInvocation = "Ambiguous invocation of indexer in type '{0}'"; 2271 public const string IncompatibleOperand = "Operator '{0}' incompatible with operand type '{1}'"; 2272 public const string IncompatibleOperands = "Operator '{0}' incompatible with operand types '{1}' and '{2}'"; 2273 public const string UnterminatedStringLiteral = "Unterminated string literal"; 2274 public const string InvalidCharacter = "Syntax error '{0}'"; 2275 public const string DigitExpected = "Digit expected"; 2276 public const string SyntaxError = "Syntax error"; 2277 public const string TokenExpected = "{0} expected"; 2278 public const string ParseExceptionFormat = "{0} (at index {1})"; 2279 public const string ColonExpected = "':' expected"; 2280 public const string OpenParenExpected = "'(' expected"; 2281 public const string CloseParenOrOperatorExpected = "')' or operator expected"; 2282 public const string CloseParenOrCommaExpected = "')' or ',' expected"; 2283 public const string DotOrOpenParenExpected = "'.' or '(' expected"; 2284 public const string OpenBracketExpected = "'[' expected"; 2285 public const string CloseBracketOrCommaExpected = "']' or ',' expected"; 2286 public const string IdentifierExpected = "Identifier expected"; 2287 } 2288 }
var q = collection.AsQueryable<Customer>() .Where(String.Format("CustomerName = \"{0}\" And Tel != \"\"", searchWord)) .OrderBy("Tel"); return q.GetPagingList<Customer>(pagingInfo);
我很少用Linq不知道大家都用什么样的方法,有更好的方法可以给我留言~