#region License // Copyright (c) 2007 James Newton-King // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. #endregion using System; using System.Collections.Generic; #if HAVE_BIG_INTEGER using System.Numerics; #endif using System.Reflection; using System.Collections; using System.Globalization; using System.Text; using System.Runtime.CompilerServices; using System.Diagnostics.CodeAnalysis; #if !HAVE_LINQ using LC.Newtonsoft.Json.Utilities.LinqBridge; #else using System.Linq; #endif using LC.Newtonsoft.Json.Serialization; namespace LC.Newtonsoft.Json.Utilities { #if (DOTNET || PORTABLE || PORTABLE40) && !NETSTANDARD2_0 [Flags] internal enum MemberTypes { Event = 2, Field = 4, Method = 8, Property = 16 } #endif #if PORTABLE && !NETSTANDARD2_0 [Flags] internal enum BindingFlags { Default = 0, IgnoreCase = 1, DeclaredOnly = 2, Instance = 4, Static = 8, Public = 16, NonPublic = 32, FlattenHierarchy = 64, InvokeMethod = 256, CreateInstance = 512, GetField = 1024, SetField = 2048, GetProperty = 4096, SetProperty = 8192, PutDispProperty = 16384, ExactBinding = 65536, PutRefDispProperty = 32768, SuppressChangeType = 131072, OptionalParamBinding = 262144, IgnoreReturn = 16777216 } #endif internal static class ReflectionUtils { public static readonly Type[] EmptyTypes; static ReflectionUtils() { #if HAVE_EMPTY_TYPES EmptyTypes = Type.EmptyTypes; #else EmptyTypes = CollectionUtils.ArrayEmpty(); #endif } public static bool IsVirtual(this PropertyInfo propertyInfo) { ValidationUtils.ArgumentNotNull(propertyInfo, nameof(propertyInfo)); MethodInfo? m = propertyInfo.GetGetMethod(true); if (m != null && m.IsVirtual) { return true; } m = propertyInfo.GetSetMethod(true); if (m != null && m.IsVirtual) { return true; } return false; } public static MethodInfo? GetBaseDefinition(this PropertyInfo propertyInfo) { ValidationUtils.ArgumentNotNull(propertyInfo, nameof(propertyInfo)); MethodInfo? m = propertyInfo.GetGetMethod(true); if (m != null) { return m.GetBaseDefinition(); } return propertyInfo.GetSetMethod(true)?.GetBaseDefinition(); } public static bool IsPublic(PropertyInfo property) { var getMethod = property.GetGetMethod(); if (getMethod != null && getMethod.IsPublic) { return true; } var setMethod = property.GetSetMethod(); if (setMethod != null && setMethod.IsPublic) { return true; } return false; } public static Type? GetObjectType(object? v) { return v?.GetType(); } public static string GetTypeName(Type t, TypeNameAssemblyFormatHandling assemblyFormat, ISerializationBinder? binder) { string fullyQualifiedTypeName = GetFullyQualifiedTypeName(t, binder); switch (assemblyFormat) { case TypeNameAssemblyFormatHandling.Simple: return RemoveAssemblyDetails(fullyQualifiedTypeName); case TypeNameAssemblyFormatHandling.Full: return fullyQualifiedTypeName; default: throw new ArgumentOutOfRangeException(); } } private static string GetFullyQualifiedTypeName(Type t, ISerializationBinder? binder) { if (binder != null) { binder.BindToName(t, out string? assemblyName, out string? typeName); #if (NET20 || NET35) // for older SerializationBinder implementations that didn't have BindToName if (assemblyName == null & typeName == null) { return t.AssemblyQualifiedName; } #endif return typeName + (assemblyName == null ? "" : ", " + assemblyName); } return t.AssemblyQualifiedName; } private static string RemoveAssemblyDetails(string fullyQualifiedTypeName) { StringBuilder builder = new StringBuilder(); // loop through the type name and filter out qualified assembly details from nested type names bool writingAssemblyName = false; bool skippingAssemblyDetails = false; for (int i = 0; i < fullyQualifiedTypeName.Length; i++) { char current = fullyQualifiedTypeName[i]; switch (current) { case '[': case ']': writingAssemblyName = false; skippingAssemblyDetails = false; builder.Append(current); break; case ',': if (!writingAssemblyName) { writingAssemblyName = true; builder.Append(current); } else { skippingAssemblyDetails = true; } break; default: if (!skippingAssemblyDetails) { builder.Append(current); } break; } } return builder.ToString(); } public static bool HasDefaultConstructor(Type t, bool nonPublic) { ValidationUtils.ArgumentNotNull(t, nameof(t)); if (t.IsValueType()) { return true; } return (GetDefaultConstructor(t, nonPublic) != null); } public static ConstructorInfo GetDefaultConstructor(Type t) { return GetDefaultConstructor(t, false); } public static ConstructorInfo GetDefaultConstructor(Type t, bool nonPublic) { BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public; if (nonPublic) { bindingFlags = bindingFlags | BindingFlags.NonPublic; } return t.GetConstructors(bindingFlags).SingleOrDefault(c => !c.GetParameters().Any()); } public static bool IsNullable(Type t) { ValidationUtils.ArgumentNotNull(t, nameof(t)); if (t.IsValueType()) { return IsNullableType(t); } return true; } public static bool IsNullableType(Type t) { ValidationUtils.ArgumentNotNull(t, nameof(t)); return (t.IsGenericType() && t.GetGenericTypeDefinition() == typeof(Nullable<>)); } public static Type EnsureNotNullableType(Type t) { return (IsNullableType(t)) ? Nullable.GetUnderlyingType(t) : t; } public static Type EnsureNotByRefType(Type t) { return (t.IsByRef && t.HasElementType) ? t.GetElementType() : t; } public static bool IsGenericDefinition(Type type, Type genericInterfaceDefinition) { if (!type.IsGenericType()) { return false; } Type t = type.GetGenericTypeDefinition(); return (t == genericInterfaceDefinition); } public static bool ImplementsGenericDefinition(Type type, Type genericInterfaceDefinition) { return ImplementsGenericDefinition(type, genericInterfaceDefinition, out _); } public static bool ImplementsGenericDefinition(Type type, Type genericInterfaceDefinition, [NotNullWhen(true)]out Type? implementingType) { ValidationUtils.ArgumentNotNull(type, nameof(type)); ValidationUtils.ArgumentNotNull(genericInterfaceDefinition, nameof(genericInterfaceDefinition)); if (!genericInterfaceDefinition.IsInterface() || !genericInterfaceDefinition.IsGenericTypeDefinition()) { throw new ArgumentNullException("'{0}' is not a generic interface definition.".FormatWith(CultureInfo.InvariantCulture, genericInterfaceDefinition)); } if (type.IsInterface()) { if (type.IsGenericType()) { Type interfaceDefinition = type.GetGenericTypeDefinition(); if (genericInterfaceDefinition == interfaceDefinition) { implementingType = type; return true; } } } foreach (Type i in type.GetInterfaces()) { if (i.IsGenericType()) { Type interfaceDefinition = i.GetGenericTypeDefinition(); if (genericInterfaceDefinition == interfaceDefinition) { implementingType = i; return true; } } } implementingType = null; return false; } public static bool InheritsGenericDefinition(Type type, Type genericClassDefinition) { return InheritsGenericDefinition(type, genericClassDefinition, out _); } public static bool InheritsGenericDefinition(Type type, Type genericClassDefinition, out Type? implementingType) { ValidationUtils.ArgumentNotNull(type, nameof(type)); ValidationUtils.ArgumentNotNull(genericClassDefinition, nameof(genericClassDefinition)); if (!genericClassDefinition.IsClass() || !genericClassDefinition.IsGenericTypeDefinition()) { throw new ArgumentNullException("'{0}' is not a generic class definition.".FormatWith(CultureInfo.InvariantCulture, genericClassDefinition)); } return InheritsGenericDefinitionInternal(type, genericClassDefinition, out implementingType); } private static bool InheritsGenericDefinitionInternal(Type currentType, Type genericClassDefinition, out Type? implementingType) { do { if (currentType.IsGenericType() && genericClassDefinition == currentType.GetGenericTypeDefinition()) { implementingType = currentType; return true; } currentType = currentType.BaseType(); } while (currentType != null); implementingType = null; return false; } /// /// Gets the type of the typed collection's items. /// /// The type. /// The type of the typed collection's items. public static Type? GetCollectionItemType(Type type) { ValidationUtils.ArgumentNotNull(type, nameof(type)); if (type.IsArray) { return type.GetElementType(); } if (ImplementsGenericDefinition(type, typeof(IEnumerable<>), out Type? genericListType)) { if (genericListType!.IsGenericTypeDefinition()) { throw new Exception("Type {0} is not a collection.".FormatWith(CultureInfo.InvariantCulture, type)); } return genericListType!.GetGenericArguments()[0]; } if (typeof(IEnumerable).IsAssignableFrom(type)) { return null; } throw new Exception("Type {0} is not a collection.".FormatWith(CultureInfo.InvariantCulture, type)); } public static void GetDictionaryKeyValueTypes(Type dictionaryType, out Type? keyType, out Type? valueType) { ValidationUtils.ArgumentNotNull(dictionaryType, nameof(dictionaryType)); if (ImplementsGenericDefinition(dictionaryType, typeof(IDictionary<,>), out Type? genericDictionaryType)) { if (genericDictionaryType!.IsGenericTypeDefinition()) { throw new Exception("Type {0} is not a dictionary.".FormatWith(CultureInfo.InvariantCulture, dictionaryType)); } Type[] dictionaryGenericArguments = genericDictionaryType!.GetGenericArguments(); keyType = dictionaryGenericArguments[0]; valueType = dictionaryGenericArguments[1]; return; } if (typeof(IDictionary).IsAssignableFrom(dictionaryType)) { keyType = null; valueType = null; return; } throw new Exception("Type {0} is not a dictionary.".FormatWith(CultureInfo.InvariantCulture, dictionaryType)); } /// /// Gets the member's underlying type. /// /// The member. /// The underlying type of the member. public static Type GetMemberUnderlyingType(MemberInfo member) { ValidationUtils.ArgumentNotNull(member, nameof(member)); switch (member.MemberType()) { case MemberTypes.Field: return ((FieldInfo)member).FieldType; case MemberTypes.Property: return ((PropertyInfo)member).PropertyType; case MemberTypes.Event: return ((EventInfo)member).EventHandlerType; case MemberTypes.Method: return ((MethodInfo)member).ReturnType; default: throw new ArgumentException("MemberInfo must be of type FieldInfo, PropertyInfo, EventInfo or MethodInfo", nameof(member)); } } public static bool IsByRefLikeType(Type type) { if (!type.IsValueType()) { return false; } // IsByRefLike flag on type is not available in netstandard2.0 Attribute[] attributes = GetAttributes(type, null, false); for (int i = 0; i < attributes.Length; i++) { if (string.Equals(attributes[i].GetType().FullName, "System.Runtime.CompilerServices.IsByRefLikeAttribute", StringComparison.Ordinal)) { return true; } } return false; } /// /// Determines whether the property is an indexed property. /// /// The property. /// /// true if the property is an indexed property; otherwise, false. /// public static bool IsIndexedProperty(PropertyInfo property) { ValidationUtils.ArgumentNotNull(property, nameof(property)); return (property.GetIndexParameters().Length > 0); } /// /// Gets the member's value on the object. /// /// The member. /// The target object. /// The member's value on the object. public static object GetMemberValue(MemberInfo member, object target) { ValidationUtils.ArgumentNotNull(member, nameof(member)); ValidationUtils.ArgumentNotNull(target, nameof(target)); switch (member.MemberType()) { case MemberTypes.Field: return ((FieldInfo)member).GetValue(target); case MemberTypes.Property: try { return ((PropertyInfo)member).GetValue(target, null); } catch (TargetParameterCountException e) { throw new ArgumentException("MemberInfo '{0}' has index parameters".FormatWith(CultureInfo.InvariantCulture, member.Name), e); } default: throw new ArgumentException("MemberInfo '{0}' is not of type FieldInfo or PropertyInfo".FormatWith(CultureInfo.InvariantCulture, member.Name), nameof(member)); } } /// /// Sets the member's value on the target object. /// /// The member. /// The target. /// The value. public static void SetMemberValue(MemberInfo member, object target, object? value) { ValidationUtils.ArgumentNotNull(member, nameof(member)); ValidationUtils.ArgumentNotNull(target, nameof(target)); switch (member.MemberType()) { case MemberTypes.Field: ((FieldInfo)member).SetValue(target, value); break; case MemberTypes.Property: ((PropertyInfo)member).SetValue(target, value, null); break; default: throw new ArgumentException("MemberInfo '{0}' must be of type FieldInfo or PropertyInfo".FormatWith(CultureInfo.InvariantCulture, member.Name), nameof(member)); } } /// /// Determines whether the specified MemberInfo can be read. /// /// The MemberInfo to determine whether can be read. /// /// if set to true then allow the member to be gotten non-publicly. /// /// true if the specified MemberInfo can be read; otherwise, false. /// public static bool CanReadMemberValue(MemberInfo member, bool nonPublic) { switch (member.MemberType()) { case MemberTypes.Field: FieldInfo fieldInfo = (FieldInfo)member; if (nonPublic) { return true; } else if (fieldInfo.IsPublic) { return true; } return false; case MemberTypes.Property: PropertyInfo propertyInfo = (PropertyInfo)member; if (!propertyInfo.CanRead) { return false; } if (nonPublic) { return true; } return (propertyInfo.GetGetMethod(nonPublic) != null); default: return false; } } /// /// Determines whether the specified MemberInfo can be set. /// /// The MemberInfo to determine whether can be set. /// if set to true then allow the member to be set non-publicly. /// if set to true then allow the member to be set if read-only. /// /// true if the specified MemberInfo can be set; otherwise, false. /// public static bool CanSetMemberValue(MemberInfo member, bool nonPublic, bool canSetReadOnly) { switch (member.MemberType()) { case MemberTypes.Field: FieldInfo fieldInfo = (FieldInfo)member; if (fieldInfo.IsLiteral) { return false; } if (fieldInfo.IsInitOnly && !canSetReadOnly) { return false; } if (nonPublic) { return true; } if (fieldInfo.IsPublic) { return true; } return false; case MemberTypes.Property: PropertyInfo propertyInfo = (PropertyInfo)member; if (!propertyInfo.CanWrite) { return false; } if (nonPublic) { return true; } return (propertyInfo.GetSetMethod(nonPublic) != null); default: return false; } } public static List GetFieldsAndProperties(Type type, BindingFlags bindingAttr) { List targetMembers = new List(); targetMembers.AddRange(GetFields(type, bindingAttr)); targetMembers.AddRange(GetProperties(type, bindingAttr)); // for some reason .NET returns multiple members when overriding a generic member on a base class // http://social.msdn.microsoft.com/Forums/en-US/b5abbfee-e292-4a64-8907-4e3f0fb90cd9/reflection-overriden-abstract-generic-properties?forum=netfxbcl // filter members to only return the override on the topmost class // update: I think this is fixed in .NET 3.5 SP1 - leave this in for now... List distinctMembers = new List(targetMembers.Count); foreach (IGrouping groupedMember in targetMembers.GroupBy(m => m.Name)) { int count = groupedMember.Count(); if (count == 1) { distinctMembers.Add(groupedMember.First()); } else { List resolvedMembers = new List(); foreach (MemberInfo memberInfo in groupedMember) { // this is a bit hacky // if the hiding property is hiding a base property and it is virtual // then this ensures the derived property gets used if (resolvedMembers.Count == 0) { resolvedMembers.Add(memberInfo); } else if (!IsOverridenGenericMember(memberInfo, bindingAttr) || memberInfo.Name == "Item") { // two members with the same name were declared on a type // this can be done via IL emit, e.g. Moq if (resolvedMembers.Any(m => m.DeclaringType == memberInfo.DeclaringType)) { continue; } resolvedMembers.Add(memberInfo); } } distinctMembers.AddRange(resolvedMembers); } } return distinctMembers; } private static bool IsOverridenGenericMember(MemberInfo memberInfo, BindingFlags bindingAttr) { if (memberInfo.MemberType() != MemberTypes.Property) { return false; } PropertyInfo propertyInfo = (PropertyInfo)memberInfo; if (!IsVirtual(propertyInfo)) { return false; } Type declaringType = propertyInfo.DeclaringType; if (!declaringType.IsGenericType()) { return false; } Type genericTypeDefinition = declaringType.GetGenericTypeDefinition(); if (genericTypeDefinition == null) { return false; } MemberInfo[] members = genericTypeDefinition.GetMember(propertyInfo.Name, bindingAttr); if (members.Length == 0) { return false; } Type memberUnderlyingType = GetMemberUnderlyingType(members[0]); if (!memberUnderlyingType.IsGenericParameter) { return false; } return true; } public static T? GetAttribute(object attributeProvider) where T : Attribute { return GetAttribute(attributeProvider, true); } public static T? GetAttribute(object attributeProvider, bool inherit) where T : Attribute { T[] attributes = GetAttributes(attributeProvider, inherit); return attributes?.FirstOrDefault(); } #if !(DOTNET || PORTABLE) || NETSTANDARD2_0 public static T[] GetAttributes(object attributeProvider, bool inherit) where T : Attribute { Attribute[] a = GetAttributes(attributeProvider, typeof(T), inherit); if (a is T[] attributes) { return attributes; } return a.Cast().ToArray(); } public static Attribute[] GetAttributes(object attributeProvider, Type? attributeType, bool inherit) { ValidationUtils.ArgumentNotNull(attributeProvider, nameof(attributeProvider)); object provider = attributeProvider; // http://hyperthink.net/blog/getcustomattributes-gotcha/ // ICustomAttributeProvider doesn't do inheritance switch (provider) { case Type t: object[] array = attributeType != null ? t.GetCustomAttributes(attributeType, inherit) : t.GetCustomAttributes(inherit); Attribute[] attributes = array.Cast().ToArray(); #if (NET20 || NET35) // ye olde .NET GetCustomAttributes doesn't respect the inherit argument if (inherit && t.BaseType != null) { attributes = attributes.Union(GetAttributes(t.BaseType, attributeType, inherit)).ToArray(); } #endif return attributes; case Assembly a: return (attributeType != null) ? Attribute.GetCustomAttributes(a, attributeType) : Attribute.GetCustomAttributes(a); case MemberInfo mi: return (attributeType != null) ? Attribute.GetCustomAttributes(mi, attributeType, inherit) : Attribute.GetCustomAttributes(mi, inherit); #if !PORTABLE40 case Module m: return (attributeType != null) ? Attribute.GetCustomAttributes(m, attributeType, inherit) : Attribute.GetCustomAttributes(m, inherit); #endif case ParameterInfo p: return (attributeType != null) ? Attribute.GetCustomAttributes(p, attributeType, inherit) : Attribute.GetCustomAttributes(p, inherit); default: #if !PORTABLE40 ICustomAttributeProvider customAttributeProvider = (ICustomAttributeProvider)attributeProvider; object[] result = (attributeType != null) ? customAttributeProvider.GetCustomAttributes(attributeType, inherit) : customAttributeProvider.GetCustomAttributes(inherit); return (Attribute[])result; #else throw new Exception("Cannot get attributes from '{0}'.".FormatWith(CultureInfo.InvariantCulture, provider)); #endif } } #else public static T[] GetAttributes(object attributeProvider, bool inherit) where T : Attribute { return GetAttributes(attributeProvider, typeof(T), inherit).Cast().ToArray(); } public static Attribute[] GetAttributes(object provider, Type? attributeType, bool inherit) { switch (provider) { case Type t: return (attributeType != null) ? t.GetTypeInfo().GetCustomAttributes(attributeType, inherit).ToArray() : t.GetTypeInfo().GetCustomAttributes(inherit).ToArray(); case Assembly a: return (attributeType != null) ? a.GetCustomAttributes(attributeType).ToArray() : a.GetCustomAttributes().ToArray(); case MemberInfo memberInfo: return (attributeType != null) ? memberInfo.GetCustomAttributes(attributeType, inherit).ToArray() : memberInfo.GetCustomAttributes(inherit).ToArray(); case Module module: return (attributeType != null) ? module.GetCustomAttributes(attributeType).ToArray() : module.GetCustomAttributes().ToArray(); case ParameterInfo parameterInfo: return (attributeType != null) ? parameterInfo.GetCustomAttributes(attributeType, inherit).ToArray() : parameterInfo.GetCustomAttributes(inherit).ToArray(); } throw new Exception("Cannot get attributes from '{0}'.".FormatWith(CultureInfo.InvariantCulture, provider)); } #endif public static StructMultiKey SplitFullyQualifiedTypeName(string fullyQualifiedTypeName) { int? assemblyDelimiterIndex = GetAssemblyDelimiterIndex(fullyQualifiedTypeName); string typeName; string? assemblyName; if (assemblyDelimiterIndex != null) { typeName = fullyQualifiedTypeName.Trim(0, assemblyDelimiterIndex.GetValueOrDefault()); assemblyName = fullyQualifiedTypeName.Trim(assemblyDelimiterIndex.GetValueOrDefault() + 1, fullyQualifiedTypeName.Length - assemblyDelimiterIndex.GetValueOrDefault() - 1); } else { typeName = fullyQualifiedTypeName; assemblyName = null; } return new StructMultiKey(assemblyName, typeName); } private static int? GetAssemblyDelimiterIndex(string fullyQualifiedTypeName) { // we need to get the first comma following all surrounded in brackets because of generic types // e.g. System.Collections.Generic.Dictionary`2[[System.String, mscorlib,Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 int scope = 0; for (int i = 0; i < fullyQualifiedTypeName.Length; i++) { char current = fullyQualifiedTypeName[i]; switch (current) { case '[': scope++; break; case ']': scope--; break; case ',': if (scope == 0) { return i; } break; } } return null; } public static MemberInfo GetMemberInfoFromType(Type targetType, MemberInfo memberInfo) { const BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; switch (memberInfo.MemberType()) { case MemberTypes.Property: PropertyInfo propertyInfo = (PropertyInfo)memberInfo; Type[] types = propertyInfo.GetIndexParameters().Select(p => p.ParameterType).ToArray(); return targetType.GetProperty(propertyInfo.Name, bindingAttr, null, propertyInfo.PropertyType, types, null); default: return targetType.GetMember(memberInfo.Name, memberInfo.MemberType(), bindingAttr).SingleOrDefault(); } } public static IEnumerable GetFields(Type targetType, BindingFlags bindingAttr) { ValidationUtils.ArgumentNotNull(targetType, nameof(targetType)); List fieldInfos = new List(targetType.GetFields(bindingAttr)); #if !PORTABLE // Type.GetFields doesn't return inherited private fields // manually find private fields from base class GetChildPrivateFields(fieldInfos, targetType, bindingAttr); #endif return fieldInfos.Cast(); } #if !PORTABLE private static void GetChildPrivateFields(IList initialFields, Type targetType, BindingFlags bindingAttr) { // fix weirdness with private FieldInfos only being returned for the current Type // find base type fields and add them to result if ((bindingAttr & BindingFlags.NonPublic) != 0) { // modify flags to not search for public fields BindingFlags nonPublicBindingAttr = bindingAttr.RemoveFlag(BindingFlags.Public); while ((targetType = targetType.BaseType()) != null) { // filter out protected fields IEnumerable childPrivateFields = targetType.GetFields(nonPublicBindingAttr).Where(f => f.IsPrivate); initialFields.AddRange(childPrivateFields); } } } #endif public static IEnumerable GetProperties(Type targetType, BindingFlags bindingAttr) { ValidationUtils.ArgumentNotNull(targetType, nameof(targetType)); List propertyInfos = new List(targetType.GetProperties(bindingAttr)); // GetProperties on an interface doesn't return properties from its interfaces if (targetType.IsInterface()) { foreach (Type i in targetType.GetInterfaces()) { propertyInfos.AddRange(i.GetProperties(bindingAttr)); } } GetChildPrivateProperties(propertyInfos, targetType, bindingAttr); // a base class private getter/setter will be inaccessible unless the property was gotten from the base class for (int i = 0; i < propertyInfos.Count; i++) { PropertyInfo member = propertyInfos[i]; if (member.DeclaringType != targetType) { PropertyInfo declaredMember = (PropertyInfo)GetMemberInfoFromType(member.DeclaringType, member); propertyInfos[i] = declaredMember; } } return propertyInfos; } public static BindingFlags RemoveFlag(this BindingFlags bindingAttr, BindingFlags flag) { return ((bindingAttr & flag) == flag) ? bindingAttr ^ flag : bindingAttr; } private static void GetChildPrivateProperties(IList initialProperties, Type targetType, BindingFlags bindingAttr) { // fix weirdness with private PropertyInfos only being returned for the current Type // find base type properties and add them to result // also find base properties that have been hidden by subtype properties with the same name while ((targetType = targetType.BaseType()) != null) { foreach (PropertyInfo propertyInfo in targetType.GetProperties(bindingAttr)) { PropertyInfo subTypeProperty = propertyInfo; if (!subTypeProperty.IsVirtual()) { if (!IsPublic(subTypeProperty)) { // have to test on name rather than reference because instances are different // depending on the type that GetProperties was called on int index = initialProperties.IndexOf(p => p.Name == subTypeProperty.Name); if (index == -1) { initialProperties.Add(subTypeProperty); } else { PropertyInfo childProperty = initialProperties[index]; // don't replace public child with private base if (!IsPublic(childProperty)) { // replace nonpublic properties for a child, but gotten from // the parent with the one from the child // the property gotten from the child will have access to private getter/setter initialProperties[index] = subTypeProperty; } } } else { int index = initialProperties.IndexOf(p => p.Name == subTypeProperty.Name && p.DeclaringType == subTypeProperty.DeclaringType); if (index == -1) { initialProperties.Add(subTypeProperty); } } } else { Type subTypePropertyDeclaringType = subTypeProperty.GetBaseDefinition()?.DeclaringType ?? subTypeProperty.DeclaringType; int index = initialProperties.IndexOf(p => p.Name == subTypeProperty.Name && p.IsVirtual() && (p.GetBaseDefinition()?.DeclaringType ?? p.DeclaringType).IsAssignableFrom(subTypePropertyDeclaringType)); // don't add a virtual property that has an override if (index == -1) { initialProperties.Add(subTypeProperty); } } } } } public static bool IsMethodOverridden(Type currentType, Type methodDeclaringType, string method) { bool isMethodOverriden = currentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Any(info => info.Name == method && // check that the method overrides the original on DynamicObjectProxy info.DeclaringType != methodDeclaringType && info.GetBaseDefinition().DeclaringType == methodDeclaringType ); return isMethodOverriden; } public static object? GetDefaultValue(Type type) { if (!type.IsValueType()) { return null; } switch (ConvertUtils.GetTypeCode(type)) { case PrimitiveTypeCode.Boolean: return false; case PrimitiveTypeCode.Char: case PrimitiveTypeCode.SByte: case PrimitiveTypeCode.Byte: case PrimitiveTypeCode.Int16: case PrimitiveTypeCode.UInt16: case PrimitiveTypeCode.Int32: case PrimitiveTypeCode.UInt32: return 0; case PrimitiveTypeCode.Int64: case PrimitiveTypeCode.UInt64: return 0L; case PrimitiveTypeCode.Single: return 0f; case PrimitiveTypeCode.Double: return 0.0; case PrimitiveTypeCode.Decimal: return 0m; case PrimitiveTypeCode.DateTime: return new DateTime(); #if HAVE_BIG_INTEGER case PrimitiveTypeCode.BigInteger: return new BigInteger(); #endif case PrimitiveTypeCode.Guid: return new Guid(); #if HAVE_DATE_TIME_OFFSET case PrimitiveTypeCode.DateTimeOffset: return new DateTimeOffset(); #endif } if (IsNullable(type)) { return null; } // possibly use IL initobj for perf here? return Activator.CreateInstance(type); } } }