diff --git a/Runtime/Utilities/ReflectionUtility.cs b/Runtime/Utilities/ReflectionUtility.cs index 8b4d3f2..b929151 100644 --- a/Runtime/Utilities/ReflectionUtility.cs +++ b/Runtime/Utilities/ReflectionUtility.cs @@ -11,19 +11,28 @@ namespace StansAssets.Foundation /// public static class ReflectionUtility { - static readonly string[] s_BuiltInAssemblyPrefixes = { "Mono.", "Unity.", "UnityEngine", "UnityEditor", "System", "mscorlib" }; + /// + /// Collection of predefined built-in assembly prefixes. Mono., UnityEditor., Unity., UnityEngine, System and mscorlib prefixes included. + /// + public static readonly string[] BuiltInAssemblyPrefixes = { "Mono.", "UnityEditor.", "Unity.", "UnityEngine", "System", "mscorlib" }; /// /// Creates an instance of the specified using that type's parameterless constructor. /// /// Full type name of the instance to create. /// New instance of the specified type. + /// parameter is null. + /// specified with doesn't have default parameterless constructor. public static object CreateInstance(string typeFullName) { + if (typeFullName == null) + throw new ArgumentNullException(nameof(typeFullName)); + var type = FindType(typeFullName); - return type != null && type.HasDefaultConstructor() - ? Activator.CreateInstance(type) - : null; + if (!type.HasDefaultConstructor()) + throw new ArgumentException($"Type {typeFullName} doesn't have default parameterless constructor."); + + return Activator.CreateInstance(type); } /// @@ -31,40 +40,45 @@ public static object CreateInstance(string typeFullName) /// /// Full type's name to search for. /// object found via specified . + /// parameter is null. + /// No types matching found in current application domain. public static Type FindType(string typeFullName) { + if (typeFullName == null) + throw new ArgumentNullException(nameof(typeFullName)); + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); return assemblies .SelectMany(assembly => assembly.GetTypes()) - .FirstOrDefault(type => type.FullName == null || type.FullName.Equals(typeFullName)); + .First(type => type.FullName == null || type.FullName.Equals(typeFullName)); } /// /// Searched for the implementations of the specified with . /// - /// true if the built-in assemblies have to be skipped. If set to false, no assemblies will be skipped. + /// Collection of assembly prefixes to skip. The will be ignored if its name starts with one of these prefixes. /// Specifies the whose implementations to search for. /// A collection of objects that are implementations of . - public static IEnumerable FindImplementationsOf(bool ignoreBuiltIn = false) + public static IEnumerable FindImplementationsOf(IEnumerable ignoreAssemblyPrefixes = null) { var baseType = typeof(T); - return FindImplementationsOf(baseType, ignoreBuiltIn); + return FindImplementationsOf(baseType, ignoreAssemblyPrefixes); } /// /// Gets the assemblies that have been loaded into the execution context of this application domain. /// - /// true if the built-in assemblies have to be skipped. If set to false, no assemblies will be skipped. + /// Collection of assembly prefixes to skip. The will be ignored if its name starts with one of these prefixes. /// A collection of assemblies in this application domain. - public static IEnumerable GetAssemblies(bool ignoreBuiltIn = false) + public static IEnumerable GetAssemblies(IEnumerable ignoreAssemblyPrefixes = null) { IEnumerable assemblies = AppDomain.CurrentDomain.GetAssemblies(); - if (ignoreBuiltIn) + if (ignoreAssemblyPrefixes != null) { assemblies = assemblies.Where(assembly => { var assemblyName = assembly.GetName().Name; - return !s_BuiltInAssemblyPrefixes.Any(prefix => assemblyName.StartsWith(prefix)); + return !BuiltInAssemblyPrefixes.Any(prefix => assemblyName.StartsWith(prefix)); }); } @@ -75,11 +89,15 @@ public static IEnumerable GetAssemblies(bool ignoreBuiltIn = false) /// Searched for the implementations of the specified with . /// /// Specifies the whose implementations to search for. - /// true if the built-in assemblies have to be skipped. If set to false, no assemblies will be skipped. + /// Collection of assembly prefixes to skip. The will be ignored if its name starts with one of these prefixes. /// A collection of objects that are implementations of . - public static IEnumerable FindImplementationsOf(Type baseType, bool ignoreBuiltIn = false) + /// parameter is null. + public static IEnumerable FindImplementationsOf(Type baseType, IEnumerable ignoreAssemblyPrefixes = null) { - var assemblies = GetAssemblies(ignoreBuiltIn); + if (baseType == null) + throw new ArgumentNullException(nameof(baseType)); + + var assemblies = GetAssemblies(ignoreAssemblyPrefixes); return assemblies .SelectMany(assembly => assembly.GetTypes()) @@ -93,9 +111,22 @@ public static IEnumerable FindImplementationsOf(Type baseType, bool ignore /// The string containing the name of the public property to get. /// A bitwise combination of the enumeration values that specify how the search is conducted. /// The property value of the specified object. + /// parameter is null. + /// The target object is null. + /// Property specified with the not found. public static object GetPropertyValue(object src, string propName, BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public) { - return src.GetType().GetProperty(propName, bindingAttr)?.GetValue(src, null); + if (propName == null) + throw new ArgumentNullException(nameof(propName)); + + if (src == null) + throw new TargetException($"Target {nameof(src)} object is null."); + + var property = src.GetType().GetProperty(propName, bindingAttr); + if (property == null) + throw new ArgumentException($"Property with '{propName}' name not found"); + + return property.GetValue(src, null); } /// @@ -106,9 +137,22 @@ public static object GetPropertyValue(object src, string propName, BindingFlags /// The new property value. /// A bitwise combination of the enumeration values that specify how the search is conducted. /// Specifies the of property value to set. + /// parameter is null. + /// The target object is null. + /// Property specified with the not found. public static void SetPropertyValue(object src, string propName, T propValue, BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public) { - src.GetType().GetProperty(propName, bindingAttr)?.SetValue(src, propValue); + if (propName == null) + throw new ArgumentNullException(nameof(propName)); + + if (src == null) + throw new TargetException($"Target {nameof(src)} object is null."); + + var property = src.GetType().GetProperty(propName, bindingAttr); + if (property == null) + throw new ArgumentException($"Property with '{propName}' name not found"); + + property.SetValue(src, propValue); } /// @@ -116,12 +160,12 @@ public static void SetPropertyValue(object src, string propName, T propValue, /// /// A bitwise combination of the enumeration values that specify how the search is conducted. /// true to search this member's inheritance chain to find the attributes; otherwise, false. - /// true if the built-in assemblies have to be skipped. If set to false, no assemblies will be skipped. + /// Collection of assembly prefixes to skip. The will be ignored if its name starts with one of these prefixes. /// Specifies the of the custom attribute to search for. /// A collection of objects representing all methods defined for the current that match the specified binding constraints and attributes type. - public static IEnumerable FindMethodsWithCustomAttributes(BindingFlags methodBindingFlags = BindingFlags.Instance | BindingFlags.Public, bool inherit = true, bool ignoreBuiltIn = false) where T : Attribute + public static IEnumerable FindMethodsWithCustomAttributes(BindingFlags methodBindingFlags = BindingFlags.Instance | BindingFlags.Public, bool inherit = true, IEnumerable ignoreAssemblyPrefixes = null) where T : Attribute { - var assemblies = GetAssemblies(ignoreBuiltIn); + var assemblies = GetAssemblies(ignoreAssemblyPrefixes); return assemblies .SelectMany(assembly => assembly.GetTypes()) diff --git a/Tests/Editor/Utilities/ReflectionUtilityTests.cs b/Tests/Editor/Utilities/ReflectionUtilityTests.cs index 7066834..702761e 100644 --- a/Tests/Editor/Utilities/ReflectionUtilityTests.cs +++ b/Tests/Editor/Utilities/ReflectionUtilityTests.cs @@ -95,7 +95,7 @@ public void CreateInstanceValid(Type type) [TestCase(typeof(ClassWithPrivateConstructor))] public void CreateInstanceInvalid(Type type) { - Assert.IsNull(ReflectionUtility.CreateInstance(type.FullName), $"Failed to create instance for:{type.FullName}"); + Assert.Throws(() => ReflectionUtility.CreateInstance(type.FullName), $"Failed to create instance for:{type.FullName}"); } [TestCase("System.Int32")] @@ -108,7 +108,7 @@ public void FindTypeValid(string fullTypeName) [TestCase("System.ClassDoesNotExist")] public void FindTypeInvalid(string fullTypeName) { - Assert.IsNull(ReflectionUtility.FindType(fullTypeName), $"Type {fullTypeName} not found"); + Assert.Throws(() => ReflectionUtility.FindType(fullTypeName), $"Type {fullTypeName} should NOT be found in this case"); } class TestAssembly @@ -148,7 +148,7 @@ public void GetAllAssemblies() [Test] public void GetAllAssembliesWithoutBuiltIn() { - var assemblies = ReflectionUtility.GetAssemblies(true).ToList(); + var assemblies = ReflectionUtility.GetAssemblies(ReflectionUtility.BuiltInAssemblyPrefixes).ToList(); Assert.True(assemblies.Any(), "Assemblies collection is empty"); var assembliesSearchMap = new Dictionary(); @@ -270,8 +270,8 @@ public void SetPropertyValue() [Test] public void FindMethodsWithCustomAttributes() { - var allMethodInfos = ReflectionUtility.FindMethodsWithCustomAttributes(ignoreBuiltIn: true).ToList(); - var nonInheritedMethodInfos = ReflectionUtility.FindMethodsWithCustomAttributes(inherit: false, ignoreBuiltIn: true).ToList(); + var allMethodInfos = ReflectionUtility.FindMethodsWithCustomAttributes(ignoreAssemblyPrefixes: ReflectionUtility.BuiltInAssemblyPrefixes).ToList(); + var nonInheritedMethodInfos = ReflectionUtility.FindMethodsWithCustomAttributes(inherit: false, ignoreAssemblyPrefixes: ReflectionUtility.BuiltInAssemblyPrefixes).ToList(); Assert.True(allMethodInfos.Count == 2, "Incorrect amount of methods with custom attribute (inheritances included)"); Assert.True(nonInheritedMethodInfos.Count == 1, "Incorrect amount of methods with custom attribute (inheritances NOT included)");