1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Diagnostics; 6 using System.Linq; 7 using System.Linq.Expressions; 8 using System.Reflection; 9 10 namespace System.Dynamic.Utils 11 { 12 internal static class TypeUtils 13 { 14 private static readonly Type[] s_arrayAssignableInterfaces = typeof(int[]).GetInterfaces() 15 .Where(i => i.IsGenericType) 16 .Select(i => i.GetGenericTypeDefinition()) 17 .ToArray(); 18 GetNonNullableType(this Type type)19 public static Type GetNonNullableType(this Type type) => IsNullableType(type) ? type.GetGenericArguments()[0] : type; 20 GetNullableType(this Type type)21 public static Type GetNullableType(this Type type) 22 { 23 Debug.Assert(type != null, "type cannot be null"); 24 if (type.IsValueType && !IsNullableType(type)) 25 { 26 return typeof(Nullable<>).MakeGenericType(type); 27 } 28 29 return type; 30 } 31 IsNullableType(this Type type)32 public static bool IsNullableType(this Type type) => type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); 33 IsNullableOrReferenceType(this Type type)34 public static bool IsNullableOrReferenceType(this Type type) => !type.IsValueType || IsNullableType(type); 35 IsBool(this Type type)36 public static bool IsBool(this Type type) => GetNonNullableType(type) == typeof(bool); 37 IsNumeric(this Type type)38 public static bool IsNumeric(this Type type) 39 { 40 type = GetNonNullableType(type); 41 if (!type.IsEnum) 42 { 43 switch (type.GetTypeCode()) 44 { 45 case TypeCode.Char: 46 case TypeCode.SByte: 47 case TypeCode.Byte: 48 case TypeCode.Int16: 49 case TypeCode.Int32: 50 case TypeCode.Int64: 51 case TypeCode.Double: 52 case TypeCode.Single: 53 case TypeCode.UInt16: 54 case TypeCode.UInt32: 55 case TypeCode.UInt64: 56 return true; 57 } 58 } 59 60 return false; 61 } 62 IsInteger(this Type type)63 public static bool IsInteger(this Type type) 64 { 65 type = GetNonNullableType(type); 66 if (!type.IsEnum) 67 { 68 switch (type.GetTypeCode()) 69 { 70 case TypeCode.Byte: 71 case TypeCode.SByte: 72 case TypeCode.Int16: 73 case TypeCode.Int32: 74 case TypeCode.Int64: 75 case TypeCode.UInt16: 76 case TypeCode.UInt32: 77 case TypeCode.UInt64: 78 return true; 79 } 80 } 81 82 return false; 83 } 84 IsInteger64(this Type type)85 public static bool IsInteger64(this Type type) 86 { 87 type = GetNonNullableType(type); 88 if (!type.IsEnum) 89 { 90 switch (type.GetTypeCode()) 91 { 92 case TypeCode.Int64: 93 case TypeCode.UInt64: 94 return true; 95 } 96 } 97 98 return false; 99 } 100 IsArithmetic(this Type type)101 public static bool IsArithmetic(this Type type) 102 { 103 type = GetNonNullableType(type); 104 if (!type.IsEnum) 105 { 106 switch (type.GetTypeCode()) 107 { 108 case TypeCode.Int16: 109 case TypeCode.Int32: 110 case TypeCode.Int64: 111 case TypeCode.Double: 112 case TypeCode.Single: 113 case TypeCode.UInt16: 114 case TypeCode.UInt32: 115 case TypeCode.UInt64: 116 return true; 117 } 118 } 119 120 return false; 121 } 122 IsUnsignedInt(this Type type)123 public static bool IsUnsignedInt(this Type type) 124 { 125 type = GetNonNullableType(type); 126 if (!type.IsEnum) 127 { 128 switch (type.GetTypeCode()) 129 { 130 case TypeCode.UInt16: 131 case TypeCode.UInt32: 132 case TypeCode.UInt64: 133 return true; 134 } 135 } 136 137 return false; 138 } 139 IsIntegerOrBool(this Type type)140 public static bool IsIntegerOrBool(this Type type) 141 { 142 type = GetNonNullableType(type); 143 if (!type.IsEnum) 144 { 145 switch (type.GetTypeCode()) 146 { 147 case TypeCode.Int64: 148 case TypeCode.Int32: 149 case TypeCode.Int16: 150 case TypeCode.UInt64: 151 case TypeCode.UInt32: 152 case TypeCode.UInt16: 153 case TypeCode.Boolean: 154 case TypeCode.SByte: 155 case TypeCode.Byte: 156 return true; 157 } 158 } 159 160 return false; 161 } 162 IsNumericOrBool(this Type type)163 public static bool IsNumericOrBool(this Type type) => IsNumeric(type) || IsBool(type); 164 165 // Checks if the type is a valid target for an instance call IsValidInstanceType(MemberInfo member, Type instanceType)166 public static bool IsValidInstanceType(MemberInfo member, Type instanceType) 167 { 168 Type targetType = member.DeclaringType; 169 if (AreReferenceAssignable(targetType, instanceType)) 170 { 171 return true; 172 } 173 174 if (targetType == null) 175 { 176 return false; 177 } 178 179 if (instanceType.IsValueType) 180 { 181 if (AreReferenceAssignable(targetType, typeof(object))) 182 { 183 return true; 184 } 185 186 if (AreReferenceAssignable(targetType, typeof(ValueType))) 187 { 188 return true; 189 } 190 191 if (instanceType.IsEnum && AreReferenceAssignable(targetType, typeof(Enum))) 192 { 193 return true; 194 } 195 196 // A call to an interface implemented by a struct is legal whether the struct has 197 // been boxed or not. 198 if (targetType.IsInterface) 199 { 200 foreach (Type interfaceType in instanceType.GetTypeInfo().ImplementedInterfaces) 201 { 202 if (AreReferenceAssignable(targetType, interfaceType)) 203 { 204 return true; 205 } 206 } 207 } 208 } 209 210 return false; 211 } 212 HasIdentityPrimitiveOrNullableConversionTo(this Type source, Type dest)213 public static bool HasIdentityPrimitiveOrNullableConversionTo(this Type source, Type dest) 214 { 215 Debug.Assert(source != null && dest != null); 216 217 // Identity conversion 218 if (AreEquivalent(source, dest)) 219 { 220 return true; 221 } 222 223 // Nullable conversions 224 if (IsNullableType(source) && AreEquivalent(dest, GetNonNullableType(source))) 225 { 226 return true; 227 } 228 229 if (IsNullableType(dest) && AreEquivalent(source, GetNonNullableType(dest))) 230 { 231 return true; 232 } 233 234 // Primitive runtime conversions 235 // All conversions amongst enum, bool, char, integer and float types 236 // (and their corresponding nullable types) are legal except for 237 // nonbool==>bool and nonbool==>bool? which are only legal from 238 // bool-backed enums. 239 return IsConvertible(source) && IsConvertible(dest) 240 && (GetNonNullableType(dest) != typeof(bool) 241 || source.IsEnum && source.GetEnumUnderlyingType() == typeof(bool)); 242 } 243 HasReferenceConversionTo(this Type source, Type dest)244 public static bool HasReferenceConversionTo(this Type source, Type dest) 245 { 246 Debug.Assert(source != null && dest != null); 247 248 // void -> void conversion is handled elsewhere 249 // (it's an identity conversion) 250 // All other void conversions are disallowed. 251 if (source == typeof(void) || dest == typeof(void)) 252 { 253 return false; 254 } 255 256 Type nnSourceType = GetNonNullableType(source); 257 Type nnDestType = GetNonNullableType(dest); 258 259 // Down conversion 260 if (nnSourceType.IsAssignableFrom(nnDestType)) 261 { 262 return true; 263 } 264 265 // Up conversion 266 if (nnDestType.IsAssignableFrom(nnSourceType)) 267 { 268 return true; 269 } 270 271 // Interface conversion 272 if (source.IsInterface || dest.IsInterface) 273 { 274 return true; 275 } 276 277 // Variant delegate conversion 278 if (IsLegalExplicitVariantDelegateConversion(source, dest)) 279 { 280 return true; 281 } 282 283 // Object conversion handled by assignable above. 284 Debug.Assert(source != typeof(object) && dest != typeof(object)); 285 286 return (source.IsArray || dest.IsArray) && StrictHasReferenceConversionTo(source, dest, true); 287 } 288 StrictHasReferenceConversionTo(this Type source, Type dest, bool skipNonArray)289 private static bool StrictHasReferenceConversionTo(this Type source, Type dest, bool skipNonArray) 290 { 291 // HasReferenceConversionTo was both too strict and too lax. It was too strict in prohibiting 292 // some valid conversions involving arrays, and too lax in allowing casts between interfaces 293 // and sealed classes that don't implement them. Unfortunately fixing the lax cases would be 294 // a breaking change, especially since such expressions will even work if only given null 295 // arguments. 296 // This method catches the cases that were incorrectly disallowed, but when it needs to 297 // examine possible conversions of element or type parameters it applies stricter rules. 298 299 for(;;) 300 { 301 if (!skipNonArray) // Skip if we just came from HasReferenceConversionTo and have just tested these 302 { 303 if (source.IsValueType | dest.IsValueType) 304 { 305 return false; 306 } 307 308 // Includes to case of either being typeof(object) 309 if (source.IsAssignableFrom(dest) || dest.IsAssignableFrom(source)) 310 { 311 return true; 312 } 313 314 if (source.IsInterface) 315 { 316 if (dest.IsInterface || dest.IsClass && !dest.IsSealed) 317 { 318 return true; 319 } 320 } 321 else if (dest.IsInterface) 322 { 323 if (source.IsClass && !source.IsSealed) 324 { 325 return true; 326 } 327 } 328 } 329 330 if (source.IsArray) 331 { 332 if (dest.IsArray) 333 { 334 if (source.GetArrayRank() != dest.GetArrayRank() || source.IsSZArray != dest.IsSZArray) 335 { 336 return false; 337 } 338 339 source = source.GetElementType(); 340 dest = dest.GetElementType(); 341 skipNonArray = false; 342 } 343 else 344 { 345 return HasArrayToInterfaceConversion(source, dest); 346 } 347 } 348 else if (dest.IsArray) 349 { 350 if (HasInterfaceToArrayConversion(source, dest)) 351 { 352 return true; 353 } 354 355 return IsImplicitReferenceConversion(typeof(Array), source); 356 } 357 else 358 { 359 return IsLegalExplicitVariantDelegateConversion(source, dest); 360 } 361 } 362 } 363 HasArrayToInterfaceConversion(Type source, Type dest)364 private static bool HasArrayToInterfaceConversion(Type source, Type dest) 365 { 366 Debug.Assert(source.IsArray); 367 if (!source.IsSZArray || !dest.IsInterface || !dest.IsGenericType) 368 { 369 return false; 370 } 371 372 Type[] destParams = dest.GetGenericArguments(); 373 if (destParams.Length != 1) 374 { 375 return false; 376 } 377 378 Type destGen = dest.GetGenericTypeDefinition(); 379 380 foreach (Type iface in s_arrayAssignableInterfaces) 381 { 382 if (AreEquivalent(destGen, iface)) 383 { 384 return StrictHasReferenceConversionTo(source.GetElementType(), destParams[0], false); 385 } 386 } 387 388 return false; 389 } 390 HasInterfaceToArrayConversion(Type source, Type dest)391 private static bool HasInterfaceToArrayConversion(Type source, Type dest) 392 { 393 Debug.Assert(dest.IsSZArray); 394 if (!dest.IsSZArray || !source.IsInterface || !source.IsGenericType) 395 { 396 return false; 397 } 398 399 Type[] sourceParams = source.GetGenericArguments(); 400 if (sourceParams.Length != 1) 401 { 402 return false; 403 } 404 405 Type sourceGen = source.GetGenericTypeDefinition(); 406 407 foreach (Type iface in s_arrayAssignableInterfaces) 408 { 409 if (AreEquivalent(sourceGen, iface)) 410 { 411 return StrictHasReferenceConversionTo(sourceParams[0], dest.GetElementType(), false); 412 } 413 } 414 415 return false; 416 } 417 IsCovariant(Type t)418 private static bool IsCovariant(Type t) 419 { 420 Debug.Assert(t != null); 421 return 0 != (t.GenericParameterAttributes & GenericParameterAttributes.Covariant); 422 } 423 IsContravariant(Type t)424 private static bool IsContravariant(Type t) 425 { 426 Debug.Assert(t != null); 427 return 0 != (t.GenericParameterAttributes & GenericParameterAttributes.Contravariant); 428 } 429 IsInvariant(Type t)430 private static bool IsInvariant(Type t) 431 { 432 Debug.Assert(t != null); 433 return 0 == (t.GenericParameterAttributes & GenericParameterAttributes.VarianceMask); 434 } 435 IsDelegate(Type t)436 private static bool IsDelegate(Type t) 437 { 438 Debug.Assert(t != null); 439 return t.IsSubclassOf(typeof(MulticastDelegate)); 440 } 441 IsLegalExplicitVariantDelegateConversion(Type source, Type dest)442 public static bool IsLegalExplicitVariantDelegateConversion(Type source, Type dest) 443 { 444 Debug.Assert(source != null && dest != null); 445 446 // There *might* be a legal conversion from a generic delegate type S to generic delegate type T, 447 // provided all of the follow are true: 448 // o Both types are constructed generic types of the same generic delegate type, D<X1,... Xk>. 449 // That is, S = D<S1...>, T = D<T1...>. 450 // o If type parameter Xi is declared to be invariant then Si must be identical to Ti. 451 // o If type parameter Xi is declared to be covariant ("out") then Si must be convertible 452 // to Ti via an identify conversion, implicit reference conversion, or explicit reference conversion. 453 // o If type parameter Xi is declared to be contravariant ("in") then either Si must be identical to Ti, 454 // or Si and Ti must both be reference types. 455 456 if (!IsDelegate(source) || !IsDelegate(dest) || !source.IsGenericType || !dest.IsGenericType) 457 { 458 return false; 459 } 460 461 Type genericDelegate = source.GetGenericTypeDefinition(); 462 463 if (dest.GetGenericTypeDefinition() != genericDelegate) 464 { 465 return false; 466 } 467 468 Type[] genericParameters = genericDelegate.GetGenericArguments(); 469 Type[] sourceArguments = source.GetGenericArguments(); 470 Type[] destArguments = dest.GetGenericArguments(); 471 472 Debug.Assert(genericParameters != null); 473 Debug.Assert(sourceArguments != null); 474 Debug.Assert(destArguments != null); 475 Debug.Assert(genericParameters.Length == sourceArguments.Length); 476 Debug.Assert(genericParameters.Length == destArguments.Length); 477 478 for (int iParam = 0; iParam < genericParameters.Length; ++iParam) 479 { 480 Type sourceArgument = sourceArguments[iParam]; 481 Type destArgument = destArguments[iParam]; 482 483 Debug.Assert(sourceArgument != null && destArgument != null); 484 485 // If the arguments are identical then this one is automatically good, so skip it. 486 if (AreEquivalent(sourceArgument, destArgument)) 487 { 488 continue; 489 } 490 491 Type genericParameter = genericParameters[iParam]; 492 493 Debug.Assert(genericParameter != null); 494 495 if (IsInvariant(genericParameter)) 496 { 497 return false; 498 } 499 500 if (IsCovariant(genericParameter)) 501 { 502 if (!sourceArgument.HasReferenceConversionTo(destArgument)) 503 { 504 return false; 505 } 506 } 507 else if (IsContravariant(genericParameter) && (sourceArgument.IsValueType || destArgument.IsValueType)) 508 { 509 return false; 510 } 511 } 512 513 return true; 514 } 515 IsConvertible(this Type type)516 public static bool IsConvertible(this Type type) 517 { 518 type = GetNonNullableType(type); 519 if (type.IsEnum) 520 { 521 return true; 522 } 523 524 switch (type.GetTypeCode()) 525 { 526 case TypeCode.Boolean: 527 case TypeCode.Byte: 528 case TypeCode.SByte: 529 case TypeCode.Int16: 530 case TypeCode.Int32: 531 case TypeCode.Int64: 532 case TypeCode.UInt16: 533 case TypeCode.UInt32: 534 case TypeCode.UInt64: 535 case TypeCode.Single: 536 case TypeCode.Double: 537 case TypeCode.Char: 538 return true; 539 540 default: 541 return false; 542 } 543 } 544 HasReferenceEquality(Type left, Type right)545 public static bool HasReferenceEquality(Type left, Type right) 546 { 547 if (left.IsValueType || right.IsValueType) 548 { 549 return false; 550 } 551 552 // If we have an interface and a reference type then we can do 553 // reference equality. 554 555 // If we have two reference types and one is assignable to the 556 // other then we can do reference equality. 557 558 return left.IsInterface || right.IsInterface || AreReferenceAssignable(left, right) 559 || AreReferenceAssignable(right, left); 560 } 561 HasBuiltInEqualityOperator(Type left, Type right)562 public static bool HasBuiltInEqualityOperator(Type left, Type right) 563 { 564 // If we have an interface and a reference type then we can do 565 // reference equality. 566 if (left.IsInterface && !right.IsValueType) 567 { 568 return true; 569 } 570 571 if (right.IsInterface && !left.IsValueType) 572 { 573 return true; 574 } 575 576 // If we have two reference types and one is assignable to the 577 // other then we can do reference equality. 578 if (!left.IsValueType && !right.IsValueType) 579 { 580 if (AreReferenceAssignable(left, right) || AreReferenceAssignable(right, left)) 581 { 582 return true; 583 } 584 } 585 586 // Otherwise, if the types are not the same then we definitely 587 // do not have a built-in equality operator. 588 if (!AreEquivalent(left, right)) 589 { 590 return false; 591 } 592 593 // We have two identical value types, modulo nullability. (If they were both the 594 // same reference type then we would have returned true earlier.) 595 Debug.Assert(left.IsValueType); 596 597 // Equality between struct types is only defined for numerics, bools, enums, 598 // and their nullable equivalents. 599 Type nnType = GetNonNullableType(left); 600 return nnType == typeof(bool) || IsNumeric(nnType) || nnType.IsEnum; 601 } 602 IsImplicitlyConvertibleTo(this Type source, Type destination)603 public static bool IsImplicitlyConvertibleTo(this Type source, Type destination) => 604 AreEquivalent(source, destination) // identity conversion 605 || IsImplicitNumericConversion(source, destination) 606 || IsImplicitReferenceConversion(source, destination) 607 || IsImplicitBoxingConversion(source, destination) 608 || IsImplicitNullableConversion(source, destination); 609 GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType)610 public static MethodInfo GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType) 611 { 612 Type nnExprType = GetNonNullableType(convertFrom); 613 Type nnConvType = GetNonNullableType(convertToType); 614 615 // try exact match on types 616 MethodInfo[] eMethods = nnExprType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); 617 618 MethodInfo method = FindConversionOperator(eMethods, convertFrom, convertToType); 619 if (method != null) 620 { 621 return method; 622 } 623 624 MethodInfo[] cMethods = nnConvType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); 625 626 method = FindConversionOperator(cMethods, convertFrom, convertToType); 627 if (method != null) 628 { 629 return method; 630 } 631 632 if (AreEquivalent(nnExprType, convertFrom) && AreEquivalent(nnConvType, convertToType)) 633 { 634 return null; 635 } 636 637 // try lifted conversion 638 return FindConversionOperator(eMethods, nnExprType, nnConvType) 639 ?? FindConversionOperator(cMethods, nnExprType, nnConvType) 640 ?? FindConversionOperator(eMethods, nnExprType, convertToType) 641 ?? FindConversionOperator(cMethods, nnExprType, convertToType); 642 } 643 FindConversionOperator(MethodInfo[] methods, Type typeFrom, Type typeTo)644 private static MethodInfo FindConversionOperator(MethodInfo[] methods, Type typeFrom, Type typeTo) 645 { 646 foreach (MethodInfo mi in methods) 647 { 648 if ((mi.Name == "op_Implicit" || mi.Name == "op_Explicit") && AreEquivalent(mi.ReturnType, typeTo)) 649 { 650 ParameterInfo[] pis = mi.GetParametersCached(); 651 if (pis.Length == 1 && AreEquivalent(pis[0].ParameterType, typeFrom)) 652 { 653 return mi; 654 } 655 } 656 } 657 658 return null; 659 } 660 661 [Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] IsImplicitNumericConversion(Type source, Type destination)662 private static bool IsImplicitNumericConversion(Type source, Type destination) 663 { 664 TypeCode tcSource = source.GetTypeCode(); 665 TypeCode tcDest = destination.GetTypeCode(); 666 667 switch (tcSource) 668 { 669 case TypeCode.SByte: 670 switch (tcDest) 671 { 672 case TypeCode.Int16: 673 case TypeCode.Int32: 674 case TypeCode.Int64: 675 case TypeCode.Single: 676 case TypeCode.Double: 677 case TypeCode.Decimal: 678 return true; 679 } 680 681 break; 682 case TypeCode.Byte: 683 switch (tcDest) 684 { 685 case TypeCode.Int16: 686 case TypeCode.UInt16: 687 case TypeCode.Int32: 688 case TypeCode.UInt32: 689 case TypeCode.Int64: 690 case TypeCode.UInt64: 691 case TypeCode.Single: 692 case TypeCode.Double: 693 case TypeCode.Decimal: 694 return true; 695 } 696 697 break; 698 case TypeCode.Int16: 699 switch (tcDest) 700 { 701 case TypeCode.Int32: 702 case TypeCode.Int64: 703 case TypeCode.Single: 704 case TypeCode.Double: 705 case TypeCode.Decimal: 706 return true; 707 } 708 709 break; 710 case TypeCode.UInt16: 711 switch (tcDest) 712 { 713 case TypeCode.Int32: 714 case TypeCode.UInt32: 715 case TypeCode.Int64: 716 case TypeCode.UInt64: 717 case TypeCode.Single: 718 case TypeCode.Double: 719 case TypeCode.Decimal: 720 return true; 721 } 722 723 break; 724 case TypeCode.Int32: 725 switch (tcDest) 726 { 727 case TypeCode.Int64: 728 case TypeCode.Single: 729 case TypeCode.Double: 730 case TypeCode.Decimal: 731 return true; 732 } 733 734 break; 735 case TypeCode.UInt32: 736 switch (tcDest) 737 { 738 case TypeCode.Int64: 739 case TypeCode.UInt64: 740 case TypeCode.Single: 741 case TypeCode.Double: 742 case TypeCode.Decimal: 743 return true; 744 } 745 746 break; 747 case TypeCode.Int64: 748 case TypeCode.UInt64: 749 switch (tcDest) 750 { 751 case TypeCode.Single: 752 case TypeCode.Double: 753 case TypeCode.Decimal: 754 return true; 755 } 756 757 break; 758 case TypeCode.Char: 759 switch (tcDest) 760 { 761 case TypeCode.UInt16: 762 case TypeCode.Int32: 763 case TypeCode.UInt32: 764 case TypeCode.Int64: 765 case TypeCode.UInt64: 766 case TypeCode.Single: 767 case TypeCode.Double: 768 case TypeCode.Decimal: 769 return true; 770 } 771 772 break; 773 case TypeCode.Single: 774 return tcDest == TypeCode.Double; 775 } 776 777 return false; 778 } 779 IsImplicitReferenceConversion(Type source, Type destination)780 private static bool IsImplicitReferenceConversion(Type source, Type destination) => 781 destination.IsAssignableFrom(source); 782 IsImplicitBoxingConversion(Type source, Type destination)783 private static bool IsImplicitBoxingConversion(Type source, Type destination) => 784 source.IsValueType && (destination == typeof(object) || destination == typeof(ValueType)) || source.IsEnum && destination == typeof(Enum); 785 IsImplicitNullableConversion(Type source, Type destination)786 private static bool IsImplicitNullableConversion(Type source, Type destination) => 787 IsNullableType(destination) && IsImplicitlyConvertibleTo(GetNonNullableType(source), GetNonNullableType(destination)); 788 FindGenericType(Type definition, Type type)789 public static Type FindGenericType(Type definition, Type type) 790 { 791 while ((object)type != null && type != typeof(object)) 792 { 793 if (type.IsConstructedGenericType && AreEquivalent(type.GetGenericTypeDefinition(), definition)) 794 { 795 return type; 796 } 797 798 if (definition.IsInterface) 799 { 800 foreach (Type itype in type.GetTypeInfo().ImplementedInterfaces) 801 { 802 Type found = FindGenericType(definition, itype); 803 if (found != null) 804 { 805 return found; 806 } 807 } 808 } 809 810 type = type.BaseType; 811 } 812 813 return null; 814 } 815 816 /// <summary> 817 /// Searches for an operator method on the type. The method must have 818 /// the specified signature, no generic arguments, and have the 819 /// SpecialName bit set. Also searches inherited operator methods. 820 /// 821 /// NOTE: This was designed to satisfy the needs of op_True and 822 /// op_False, because we have to do runtime lookup for those. It may 823 /// not work right for unary operators in general. 824 /// </summary> GetBooleanOperator(Type type, string name)825 public static MethodInfo GetBooleanOperator(Type type, string name) 826 { 827 do 828 { 829 MethodInfo result = type.GetAnyStaticMethodValidated(name, new[] { type }); 830 if (result != null && result.IsSpecialName && !result.ContainsGenericParameters) 831 { 832 return result; 833 } 834 835 type = type.BaseType; 836 } while (type != null); 837 838 return null; 839 } 840 GetNonRefType(this Type type)841 public static Type GetNonRefType(this Type type) => type.IsByRef ? type.GetElementType() : type; 842 AreEquivalent(Type t1, Type t2)843 public static bool AreEquivalent(Type t1, Type t2) => t1 != null && t1.IsEquivalentTo(t2); 844 AreReferenceAssignable(Type dest, Type src)845 public static bool AreReferenceAssignable(Type dest, Type src) 846 { 847 // This actually implements "Is this identity assignable and/or reference assignable?" 848 if (AreEquivalent(dest, src)) 849 { 850 return true; 851 } 852 853 return !dest.IsValueType && !src.IsValueType && dest.IsAssignableFrom(src); 854 } 855 IsSameOrSubclass(Type type, Type subType)856 public static bool IsSameOrSubclass(Type type, Type subType) => 857 AreEquivalent(type, subType) || subType.IsSubclassOf(type); 858 ValidateType(Type type, string paramName)859 public static void ValidateType(Type type, string paramName) => ValidateType(type, paramName, false, false); 860 ValidateType(Type type, string paramName, bool allowByRef, bool allowPointer)861 public static void ValidateType(Type type, string paramName, bool allowByRef, bool allowPointer) 862 { 863 if (ValidateType(type, paramName, -1)) 864 { 865 if (!allowByRef && type.IsByRef) 866 { 867 throw System.Linq.Expressions.Error.TypeMustNotBeByRef(paramName); 868 } 869 870 if (!allowPointer && type.IsPointer) 871 { 872 throw System.Linq.Expressions.Error.TypeMustNotBePointer(paramName); 873 } 874 } 875 } 876 ValidateType(Type type, string paramName, int index)877 public static bool ValidateType(Type type, string paramName, int index) 878 { 879 if (type == typeof(void)) 880 { 881 return false; // Caller can skip further checks. 882 } 883 884 if (type.ContainsGenericParameters) 885 { 886 throw type.IsGenericTypeDefinition 887 ? System.Linq.Expressions.Error.TypeIsGeneric(type, paramName, index) 888 : System.Linq.Expressions.Error.TypeContainsGenericParameters(type, paramName, index); 889 } 890 891 return true; 892 } 893 GetInvokeMethod(this Type delegateType)894 public static MethodInfo GetInvokeMethod(this Type delegateType) 895 { 896 Debug.Assert(typeof(Delegate).IsAssignableFrom(delegateType)); 897 return delegateType.GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 898 } 899 900 #if FEATURE_COMPILE 901 IsUnsigned(this Type type)902 internal static bool IsUnsigned(this Type type) => IsUnsigned(GetNonNullableType(type).GetTypeCode()); 903 IsUnsigned(this TypeCode typeCode)904 internal static bool IsUnsigned(this TypeCode typeCode) 905 { 906 switch (typeCode) 907 { 908 case TypeCode.Byte: 909 case TypeCode.UInt16: 910 case TypeCode.Char: 911 case TypeCode.UInt32: 912 case TypeCode.UInt64: 913 return true; 914 915 default: 916 return false; 917 } 918 } 919 IsFloatingPoint(this Type type)920 internal static bool IsFloatingPoint(this Type type) => IsFloatingPoint(GetNonNullableType(type).GetTypeCode()); 921 IsFloatingPoint(this TypeCode typeCode)922 internal static bool IsFloatingPoint(this TypeCode typeCode) 923 { 924 switch (typeCode) 925 { 926 case TypeCode.Single: 927 case TypeCode.Double: 928 return true; 929 930 default: 931 return false; 932 } 933 } 934 935 #endif 936 } 937 } 938