// dnlib: See LICENSE.txt for more info using System; using System.Diagnostics; using System.Reflection; using System.Text; namespace dnlib.DotNet { /// /// Extension methods for reflection types, methods, fields /// static class ReflectionExtensions { public static void GetTypeNamespaceAndName_TypeDefOrRef(this Type type, out string @namespace, out string name) { Debug.Assert(type.IsTypeDef()); name = Unescape(type.Name) ?? string.Empty; if (!type.IsNested) @namespace = type.Namespace ?? string.Empty; else { var declTypeFullName = Unescape(type.DeclaringType.FullName); var typeFullName = Unescape(type.FullName); if (declTypeFullName.Length + 1 + name.Length == typeFullName.Length) @namespace = string.Empty; else @namespace = typeFullName.Substring(declTypeFullName.Length + 1, typeFullName.Length - declTypeFullName.Length - 1 - name.Length - 1); } } /// /// Checks whether it's a /// /// The type public static bool IsSZArray(this Type self) { if (self is null || !self.IsArray) return false; var prop = self.GetType().GetProperty("IsSzArray", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (prop is not null) return (bool)prop.GetValue(self, Array2.Empty()); return (self.Name ?? string.Empty).EndsWith("[]"); } /// /// Gets a 's /// /// The type /// The type's element type public static ElementType GetElementType2(this Type a) { if (a is null) return ElementType.End; // Any invalid one is good enough if (a.IsArray) return IsSZArray(a) ? ElementType.SZArray : ElementType.Array; if (a.IsByRef) return ElementType.ByRef; if (a.IsPointer) return ElementType.Ptr; if (a.IsGenericParameter) return a.DeclaringMethod is null ? ElementType.Var : ElementType.MVar; if (a.IsGenericType && !a.IsGenericTypeDefinition) return ElementType.GenericInst; if (a == typeof(void)) return ElementType.Void; if (a == typeof(bool)) return ElementType.Boolean; if (a == typeof(char)) return ElementType.Char; if (a == typeof(sbyte)) return ElementType.I1; if (a == typeof(byte)) return ElementType.U1; if (a == typeof(short)) return ElementType.I2; if (a == typeof(ushort)) return ElementType.U2; if (a == typeof(int)) return ElementType.I4; if (a == typeof(uint)) return ElementType.U4; if (a == typeof(long)) return ElementType.I8; if (a == typeof(ulong)) return ElementType.U8; if (a == typeof(float)) return ElementType.R4; if (a == typeof(double)) return ElementType.R8; if (a == typeof(string)) return ElementType.String; if (a == typeof(TypedReference))return ElementType.TypedByRef; if (a == typeof(IntPtr)) return ElementType.I; if (a == typeof(UIntPtr)) return ElementType.U; if (a == typeof(object)) return ElementType.Object; return a.IsValueType ? ElementType.ValueType : ElementType.Class; } /// /// Returns true if is a generic type, but /// not a generic type definition, i.e., a TypeSpec. /// /// The type public static bool IsGenericButNotGenericTypeDefinition(this Type type) => type is not null && !type.IsGenericTypeDefinition && type.IsGenericType; /// /// Returns true if is a generic method, but /// not a generic method definition, i.e., a MethodSpec. /// /// The method public static bool IsGenericButNotGenericMethodDefinition(this MethodBase mb) => mb is not null && !mb.IsGenericMethodDefinition && mb.IsGenericMethod; /// /// Checks whether a parameter/prop/event type should be treated as if it is really a /// generic instance type and not a generic type definition. In the .NET metadata (method /// sig), the parameter is a generic instance type, but the CLR treats it as if it's just /// a generic type def. This seems to happen only if the parameter type is exactly the same /// type as the declaring type, eg. a method similar to: MyType<!0> MyType::SomeMethod(). /// /// Declaring type of field/method/event/property /// Field/parameter/property/event type internal static bool MustTreatTypeAsGenericInstType(this Type declaringType, Type t) => declaringType is not null && declaringType.IsGenericTypeDefinition && t == declaringType; /// /// Checks whether is a type definition and not a type spec /// (eg. pointer or generic type instantiation) /// /// this public static bool IsTypeDef(this Type type) => type is not null && !type.HasElementType && (!type.IsGenericType || type.IsGenericTypeDefinition); internal static string Unescape(string name) { if (string.IsNullOrEmpty(name) || name.IndexOf('\\') < 0) return name; var sb = new StringBuilder(name.Length); for (int i = 0; i < name.Length; i++) { if (name[i] == '\\' && i < name.Length - 1 && IsReservedTypeNameChar(name[i + 1])) sb.Append(name[++i]); else sb.Append(name[i]); } return sb.ToString(); } static bool IsReservedTypeNameChar(char c) { switch (c) { case ',': case '+': case '&': case '*': case '[': case ']': case '\\': return true; default: return false; } } } }