diff --git a/src/DryIoc/Container.cs b/src/DryIoc/Container.cs index 4540239b1..a35b5ca22 100644 --- a/src/DryIoc/Container.cs +++ b/src/DryIoc/Container.cs @@ -52,6 +52,7 @@ namespace DryIoc using System.Threading; using System.Diagnostics.CodeAnalysis; // for SuppressMessage using System.Diagnostics; // for StackTrace + using System.Runtime.CompilerServices; // for MethodImplAttribute using ImTools; using static ImTools.ArrayTools; @@ -8216,13 +8217,18 @@ public static partial class PropertiesAndFields /// Says to include properties of primitive type. /// True if property is matched and false otherwise. public static bool IsInjectable(this PropertyInfo property, - bool withNonPublic = false, bool withPrimitive = false) => - property.CanWrite - && !property.IsExplicitlyImplemented() - && !property.IsStatic() - && !property.IsIndexer() // first checks that property is assignable in general and not an indexer - && (withNonPublic || property.GetSetMethodOrNull() != null) - && (withPrimitive || !property.PropertyType.IsPrimitive(orArrayOfPrimitives: true)); + bool withNonPublic = false, bool withPrimitive = false) + { + if (!property.CanWrite || property.IsExplicitlyImplemented()) + return false; + + if (property.IsStatic()) + return false; + + return !property.IsIndexer() && + (withNonPublic || property.GetSetMethodOrNull() != null) && + (withPrimitive || !property.PropertyType.IsPrimitive(orArrayOfPrimitives: true)); + } /// Returns true if field matches flags provided. /// Field to match. @@ -9387,11 +9393,12 @@ public object GetOrTryAdd(int id, object newItem, int disposalOrder) return item; _items = _items.AddOrUpdate(id, newItem); - var disposable = newItem as IDisposable; - if (disposable != null && disposable != this) - TrackDisposable(disposable, NextDisposalIndex()); } + var disposable = newItem as IDisposable; + if (disposable != null && disposable != this) + TrackDisposable(disposable, NextDisposalIndex()); + return newItem; } @@ -9400,6 +9407,7 @@ public object GetOrTryAdd(int id, object newItem, int disposalOrder) new Scope(Parent, Name, _items, _factories, _disposables, _nextDisposalIndex); /// + [MethodImpl((MethodImplOptions)256)] public bool TryGet(out object item, int id) { if (_disposed == 1) @@ -9479,7 +9487,6 @@ public void Dispose() foreach (var disposable in disposables.Enumerate()) { // Ignoring disposing exception, as it is not important to proceed the disposal of other items - // todo: May be it is better to aggregate? try { disposable.Value.Dispose(); @@ -9507,7 +9514,7 @@ public void Dispose() private int _nextDisposalIndex; private int _disposed; - // todo: Improve perf by scaling lockers count with the items amount + // todo: Improve performance by scaling lockers count with the items amount // Sync root is required to create object only once. The same reason as for Lazy. private readonly object _locker = new object(); @@ -10933,19 +10940,26 @@ public static bool IsPrimitive(this Type type, bool orArrayOfPrimitives = false) } /// Returns all public instance constructors for the type - public static IEnumerable PublicConstructors(this Type type) => - type.GetTypeInfo().DeclaredConstructors.Match(c => c.IsPublic && !c.IsStatic); + public static IEnumerable PublicConstructors(this Type type) + { + foreach (var x in type.GetTypeInfo().DeclaredConstructors) + if (x.IsPublic && !x.IsStatic) + yield return x; + } /// Returns all public instance constructors for the type - public static IEnumerable PublicAndInternalConstructors(this Type type) => - type.GetTypeInfo().DeclaredConstructors.Match(c => !c.IsPrivate && !c.IsStatic); + public static IEnumerable PublicAndInternalConstructors(this Type type) + { + foreach (var x in type.GetTypeInfo().DeclaredConstructors) + if (!x.IsPrivate && !x.IsStatic) + yield return x; + } /// Enumerates all constructors from input type. public static IEnumerable Constructors(this Type type, bool includeNonPublic = false, bool includeStatic = false) { var ctors = type.GetTypeInfo().DeclaredConstructors.ToArrayOrSelf(); - if (ctors.Length == 0) return ctors; @@ -10978,34 +10992,31 @@ public static bool IsPrimitive(this Type type, bool orArrayOfPrimitives = false) !includeNonPublic && !ctor.IsPublic || !includeStatic && ctor.IsStatic; /// Searches and returns constructor by its signature. - public static ConstructorInfo GetConstructorOrNull(this Type type, - bool includeNonPublic = false, params Type[] args) + public static ConstructorInfo GetConstructorOrNull(this Type type, bool includeNonPublic = false, params Type[] args) { - var pTypesCount = args.Length; - var ctors = type.GetTypeInfo().DeclaredConstructors.ToArrayOrSelf(); - if (!includeNonPublic) - ctors = ctors.Match(x => x.IsPublic); - - for (var i = 0; i < ctors.Length; i++) + var argsLength = args.Length; + foreach (var ctor in type.GetTypeInfo().DeclaredConstructors) { - var ctor = ctors[i]; - var ps = ctor.GetParameters(); - if (ps.Length != pTypesCount) - continue; + if (includeNonPublic || ctor.IsPublic) + { + var ctorParams = ctor.GetParameters(); + if (ctorParams.Length != argsLength) + continue; - if (pTypesCount == 0) - return ctor; + if (argsLength == 0) + return ctor; - var p = 0; - for (; p < pTypesCount; ++p) - { - var paramType = ps[p].ParameterType; - if (paramType != args[p] && paramType.GetGenericDefinitionOrNull() != args[p]) - break; - } + var i = 0; + for (; i < argsLength; ++i) + { + var paramType = ctorParams[i].ParameterType; + if (paramType != args[i] && paramType.GetGenericDefinitionOrNull() != args[i]) + break; + } - if (p == pTypesCount) - return ctor; + if (i == argsLength) + return ctor; + } } return null; @@ -11017,23 +11028,35 @@ public static bool IsPrimitive(this Type type, bool orArrayOfPrimitives = false) /// Searches and returns constructor by its signature, or throws if not found public static ConstructorInfo Constructor(this Type type, params Type[] args) => - type.GetConstructorOrNull(includeNonPublic: true, args: args).ThrowIfNull( - Error.UnableToFindConstructorWithArgs, type, args); + type.GetConstructorOrNull(includeNonPublic: true, args: args).ThrowIfNull(Error.UnableToFindConstructorWithArgs, type, args); /// Returns single constructor otherwise (if no constructor or more than one) returns null. - public static ConstructorInfo GetSingleConstructorOrNull(this Type type, bool includeNonPublic = false) => - type.Constructors(includeNonPublic).SingleOrDefaultIfMany(); + public static ConstructorInfo GetSingleConstructorOrNull(this Type type, bool includeNonPublic = false) + { + ConstructorInfo ctor = null; + foreach (var x in type.GetTypeInfo().DeclaredConstructors) + { + if (ctor != null) // if multiple constructors + return null; + if (includeNonPublic || x.IsPublic) + ctor = x; + } + + return ctor; + } /// Returns single constructor otherwise (if no or more than one) throws an exception public static ConstructorInfo SingleConstructor(this Type type, bool includeNonPublic = false) => - type.GetSingleConstructorOrNull(includeNonPublic).ThrowIfNull( - Error.UnableToFindSingleConstructor, type, includeNonPublic); + type.GetSingleConstructorOrNull(includeNonPublic).ThrowIfNull(Error.UnableToFindSingleConstructor, type, includeNonPublic); /// Looks up for single declared method with the specified name. Returns null if method is not found. - public static MethodInfo GetSingleMethodOrNull(this Type type, string name, bool includeNonPublic = false) => - type.GetTypeInfo().DeclaredMethods - .Match(m => (includeNonPublic || m.IsPublic) && m.Name == name) - .SingleOrDefaultIfMany(); + public static MethodInfo GetSingleMethodOrNull(this Type type, string name, bool includeNonPublic = false) + { + foreach (var m in type.GetTypeInfo().DeclaredMethods) + if ((includeNonPublic || m.IsPublic) && m.Name == name) + return m; + return null; + } // note: Prefer `GetDeclaredMethod(name)` for supported platforms /// Looks for single declared (not inherited) method by name, and throws if not found. @@ -11050,30 +11073,28 @@ public static bool IsPrimitive(this Type type, bool orArrayOfPrimitives = false) public static MethodInfo GetMethodOrNull(this Type type, string name, params Type[] paramTypes) { var pTypesCount = paramTypes.Length; - var methods = type.GetTypeInfo().DeclaredMethods.ToArrayOrSelf(); - for (var i = 0; i < methods.Length; i++) + foreach (var method in type.GetTypeInfo().DeclaredMethods) { - var method = methods[i]; if (method.Name != name) continue; var ps = method.GetParameters(); - if (ps.Length != pTypesCount) - continue; + if (ps.Length == pTypesCount) + { + if (pTypesCount == 0) + return method; - if (pTypesCount == 0) - return method; + var p = 0; + for (; p < pTypesCount; ++p) + { + var paramType = ps[p].ParameterType; + if (paramType != paramTypes[p] && paramType.GetGenericDefinitionOrNull() != paramTypes[p]) + break; + } - var p = 0; - for (; p < pTypesCount; ++p) - { - var paramType = ps[p].ParameterType; - if (paramType != paramTypes[p] && paramType.GetGenericDefinitionOrNull() != paramTypes[p]) - break; + if (p == pTypesCount) + return method; } - - if (p == pTypesCount) - return method; } return null; @@ -11081,21 +11102,29 @@ public static MethodInfo GetMethodOrNull(this Type type, string name, params Typ /// Returns property by name, including inherited. Or null if not found. public static PropertyInfo Property(this Type type, string name, bool includeBase = false) => - type.GetPropertyOrNull(name, includeBase).ThrowIfNull( - Error.UndefinedPropertyWhenGettingProperty, name, type); + type.GetPropertyOrNull(name, includeBase).ThrowIfNull(Error.UndefinedPropertyWhenGettingProperty, name, type); /// Returns property by name, including inherited. Or null if not found. - public static PropertyInfo GetPropertyOrNull(this Type type, string name, bool includeBase = false) => - type.GetMembers(x => x.DeclaredProperties, includeBase: includeBase).FirstOrDefault(p => p.Name == name); + public static PropertyInfo GetPropertyOrNull(this Type type, string name, bool includeBase = false) + { + foreach (var p in type.GetTypeInfo().DeclaredProperties) + if (p.Name == name) + return p; + return !includeBase ? null : type.GetTypeInfo().BaseType?.GetPropertyOrNull(name, includeBase); + } /// Returns field by name, including inherited. Or null if not found. public static FieldInfo Field(this Type type, string name, bool includeBase = false) => - type.GetFieldOrNull(name, includeBase).ThrowIfNull( - Error.UndefinedFieldWhenGettingField, name, type); + type.GetFieldOrNull(name, includeBase).ThrowIfNull(Error.UndefinedFieldWhenGettingField, name, type); /// Returns field by name, including inherited. Or null if not found. - public static FieldInfo GetFieldOrNull(this Type type, string name, bool includeBase = false) => - type.GetMembers(x => x.DeclaredFields, includeBase: includeBase).FirstOrDefault(p => p.Name == name); + public static FieldInfo GetFieldOrNull(this Type type, string name, bool includeBase = false) + { + foreach (var f in type.GetTypeInfo().DeclaredFields) + if (f.Name == name) + return f; + return !includeBase ? null : type.GetTypeInfo().BaseType?.GetFieldOrNull(name, includeBase); + } /// Returns type assembly. public static Assembly GetAssembly(this Type type) => type.GetTypeInfo().Assembly; @@ -11104,35 +11133,42 @@ public static MethodInfo GetMethodOrNull(this Type type, string name, params Typ public static bool IsExplicitlyImplemented(this PropertyInfo property) => property.Name.Contains("."); /// Returns true if member is static, otherwise returns false. - public static bool IsStatic(this MemberInfo member) - { - var method = member as MethodInfo; - if (method != null) - return method.IsStatic; - - var field = member as FieldInfo; - if (field != null) - return field.IsStatic; - - var prop = member as PropertyInfo; - if (prop == null || prop.IsExplicitlyImplemented()) - return false; - - var propAccessor = - prop.GetGetMethodOrNull(includeNonPublic: true) ?? - prop.GetSetMethodOrNull(includeNonPublic: true); + public static bool IsStatic(this MemberInfo member) => + member is MethodInfo method ? method.IsStatic + : member is FieldInfo field ? field.IsStatic + : member is PropertyInfo prop && !prop.IsExplicitlyImplemented() && prop.IsStatic(true); + + /// Find if property is static + public static bool IsStatic(this PropertyInfo property, bool includeNonPublic = false) + { + // e.g.: set_Blah or get_Blah + var propName = property.Name; + foreach (var m in property.DeclaringType.GetTypeInfo().DeclaredMethods) + if (m.IsSpecialName && (includeNonPublic || m.IsPublic)) + { + var name = m.Name; + var nameLength = name.Length; + if (nameLength > 4 && name[3] == '_' && nameLength - 4 == propName.Length) + { + var i = 4; ; + for (var j = 0; i < nameLength; i++, j++) + if (name[i] != propName[j]) + break; + if (i == nameLength) + return m.IsStatic; + } + } - return propAccessor.IsStatic; + return false; } /// Return either , or , /// . public static Type GetReturnTypeOrDefault(this MemberInfo member) => member is ConstructorInfo ? member.DeclaringType - : member is MethodInfo ? ((MethodInfo)member).ReturnType - : member is PropertyInfo ? ((PropertyInfo)member).PropertyType - : member is FieldInfo ? ((FieldInfo)member).FieldType - : null; + : (member as MethodInfo) ?.ReturnType + ?? (member as PropertyInfo)?.PropertyType + ?? (member as FieldInfo) ?.FieldType; /// Returns true if field is backing field for property. public static bool IsBackingField(this FieldInfo field) => @@ -11349,12 +11385,24 @@ public static partial class Portable internal static IEnumerable ToTypes(IEnumerable x) => x.Select(t => t.AsType()); /// Portable version of PropertyInfo.GetGetMethod. - public static MethodInfo GetGetMethodOrNull(this PropertyInfo p, bool includeNonPublic = false) => - p.DeclaringType.GetSingleMethodOrNull("get_" + p.Name, includeNonPublic); + public static MethodInfo GetGetMethodOrNull(this PropertyInfo p, bool includeNonPublic = false) + { + var name = "get_" + p.Name; + foreach (var m in p.DeclaringType.GetTypeInfo().DeclaredMethods) + if (m.IsSpecialName && (includeNonPublic || m.IsPublic) && m.Name == name) + return m; + return null; + } /// Portable version of PropertyInfo.GetSetMethod. - public static MethodInfo GetSetMethodOrNull(this PropertyInfo p, bool includeNonPublic = false) => - p.DeclaringType.GetSingleMethodOrNull("set_" + p.Name, includeNonPublic); + public static MethodInfo GetSetMethodOrNull(this PropertyInfo p, bool includeNonPublic = false) + { + var name = "set_" + p.Name; + foreach (var m in p.DeclaringType.GetTypeInfo().DeclaredMethods) + if (m.IsSpecialName && (includeNonPublic || m.IsPublic) && m.Name == name) + return m; + return null; + } private static readonly Lazy> _getEnvCurrentManagedThreadId = Lazy.Of(() => { diff --git a/test/DryIoc.IssuesTests/Issue144_NonPublic_property _as_FactoryMethod_causes_unexplained_NRE.cs b/test/DryIoc.IssuesTests/Issue144_NonPublic_property _as_FactoryMethod_causes_unexplained_NRE.cs index e7a074f8c..f5b5eaf8f 100644 --- a/test/DryIoc.IssuesTests/Issue144_NonPublic_property _as_FactoryMethod_causes_unexplained_NRE.cs +++ b/test/DryIoc.IssuesTests/Issue144_NonPublic_property _as_FactoryMethod_causes_unexplained_NRE.cs @@ -1,10 +1,10 @@ -using System.Reflection; +using System.Reflection; using NUnit.Framework; namespace DryIoc.IssuesTests { [TestFixture] - public class Issue144_NonPublic_property__as_FactoryMethod_causes_unexplained_NRE + public class Issue144_NonPublic_property_as_FactoryMethod_causes_unexplained_NRE { [Test] public void There_should_not_be_NRE()