diff options
Diffstat (limited to 'src/SMAPI/Framework/Reflection')
-rw-r--r-- | src/SMAPI/Framework/Reflection/InterfaceProxyBuilder.cs | 138 | ||||
-rw-r--r-- | src/SMAPI/Framework/Reflection/ReflectedField.cs (renamed from src/SMAPI/Framework/Reflection/PrivateField.cs) | 16 | ||||
-rw-r--r-- | src/SMAPI/Framework/Reflection/ReflectedMethod.cs (renamed from src/SMAPI/Framework/Reflection/PrivateMethod.cs) | 16 | ||||
-rw-r--r-- | src/SMAPI/Framework/Reflection/ReflectedProperty.cs (renamed from src/SMAPI/Framework/Reflection/PrivateProperty.cs) | 42 | ||||
-rw-r--r-- | src/SMAPI/Framework/Reflection/Reflector.cs | 106 |
5 files changed, 232 insertions, 86 deletions
diff --git a/src/SMAPI/Framework/Reflection/InterfaceProxyBuilder.cs b/src/SMAPI/Framework/Reflection/InterfaceProxyBuilder.cs new file mode 100644 index 00000000..5abebc18 --- /dev/null +++ b/src/SMAPI/Framework/Reflection/InterfaceProxyBuilder.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; + +namespace StardewModdingAPI.Framework.Reflection +{ + /// <summary>Generates proxy classes to access mod APIs through an arbitrary interface.</summary> + internal class InterfaceProxyBuilder + { + /********* + ** Properties + *********/ + /// <summary>The CLR module in which to create proxy classes.</summary> + private readonly ModuleBuilder ModuleBuilder; + + /// <summary>The generated proxy types.</summary> + private readonly IDictionary<string, Type> GeneratedTypes = new Dictionary<string, Type>(); + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + public InterfaceProxyBuilder() + { + AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName($"StardewModdingAPI.Proxies, Version={this.GetType().Assembly.GetName().Version}, Culture=neutral"), AssemblyBuilderAccess.Run); + this.ModuleBuilder = assemblyBuilder.DefineDynamicModule("StardewModdingAPI.Proxies"); + } + + /// <summary>Create an API proxy.</summary> + /// <typeparam name="TInterface">The interface through which to access the API.</typeparam> + /// <param name="instance">The API instance to access.</param> + /// <param name="sourceModID">The unique ID of the mod consuming the API.</param> + /// <param name="targetModID">The unique ID of the mod providing the API.</param> + public TInterface CreateProxy<TInterface>(object instance, string sourceModID, string targetModID) + where TInterface : class + { + // validate + if (instance == null) + throw new InvalidOperationException("Can't proxy access to a null API."); + if (!typeof(TInterface).IsInterface) + throw new InvalidOperationException("The proxy type must be an interface, not a class."); + + // get proxy type + Type targetType = instance.GetType(); + string proxyTypeName = $"StardewModdingAPI.Proxies.From<{sourceModID}_{typeof(TInterface).FullName}>_To<{targetModID}_{targetType.FullName}>"; + if (!this.GeneratedTypes.TryGetValue(proxyTypeName, out Type type)) + { + type = this.CreateProxyType(proxyTypeName, typeof(TInterface), targetType); + this.GeneratedTypes[proxyTypeName] = type; + } + + // create instance + ConstructorInfo constructor = type.GetConstructor(new[] { targetType }); + if (constructor == null) + throw new InvalidOperationException($"Couldn't find the constructor for generated proxy type '{proxyTypeName}'."); // should never happen + return (TInterface)constructor.Invoke(new[] { instance }); + } + + + /********* + ** Private methods + *********/ + /// <summary>Define a class which proxies access to a target type through an interface.</summary> + /// <param name="proxyTypeName">The name of the proxy type to generate.</param> + /// <param name="interfaceType">The interface type through which to access the target.</param> + /// <param name="targetType">The target type to access.</param> + private Type CreateProxyType(string proxyTypeName, Type interfaceType, Type targetType) + { + // define proxy type + TypeBuilder proxyBuilder = this.ModuleBuilder.DefineType(proxyTypeName, TypeAttributes.Public | TypeAttributes.Class); + proxyBuilder.AddInterfaceImplementation(interfaceType); + + // create field to store target instance + FieldBuilder field = proxyBuilder.DefineField("__Target", targetType, FieldAttributes.Private); + + // create constructor which accepts target instance + { + ConstructorBuilder constructor = proxyBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard | CallingConventions.HasThis, new[] { targetType }); + ILGenerator il = constructor.GetILGenerator(); + + il.Emit(OpCodes.Ldarg_0); // this + // ReSharper disable once AssignNullToNotNullAttribute -- never null + il.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0])); // call base constructor + il.Emit(OpCodes.Ldarg_0); // this + il.Emit(OpCodes.Ldarg_1); // load argument + il.Emit(OpCodes.Stfld, field); // set field to loaded argument + il.Emit(OpCodes.Ret); + } + + // proxy methods + foreach (MethodInfo proxyMethod in interfaceType.GetMethods()) + { + var targetMethod = targetType.GetMethod(proxyMethod.Name, proxyMethod.GetParameters().Select(a => a.ParameterType).ToArray()); + if (targetMethod == null) + throw new InvalidOperationException($"The {interfaceType.FullName} interface defines method {proxyMethod.Name} which doesn't exist in the API."); + + this.ProxyMethod(proxyBuilder, targetMethod, field); + } + + // create type + return proxyBuilder.CreateType(); + } + + /// <summary>Define a method which proxies access to a method on the target.</summary> + /// <param name="proxyBuilder">The proxy type being generated.</param> + /// <param name="target">The target method.</param> + /// <param name="instanceField">The proxy field containing the API instance.</param> + private void ProxyMethod(TypeBuilder proxyBuilder, MethodInfo target, FieldBuilder instanceField) + { + Type[] argTypes = target.GetParameters().Select(a => a.ParameterType).ToArray(); + + // create method + MethodBuilder methodBuilder = proxyBuilder.DefineMethod(target.Name, MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual); + methodBuilder.SetParameters(argTypes); + methodBuilder.SetReturnType(target.ReturnType); + + // create method body + { + ILGenerator il = methodBuilder.GetILGenerator(); + + // load target instance + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, instanceField); + + // invoke target method on instance + for (int i = 0; i < argTypes.Length; i++) + il.Emit(OpCodes.Ldarg, i + 1); + il.Emit(OpCodes.Call, target); + + // return result + il.Emit(OpCodes.Ret); + } + } + } +} diff --git a/src/SMAPI/Framework/Reflection/PrivateField.cs b/src/SMAPI/Framework/Reflection/ReflectedField.cs index 0bf45969..ad1557bb 100644 --- a/src/SMAPI/Framework/Reflection/PrivateField.cs +++ b/src/SMAPI/Framework/Reflection/ReflectedField.cs @@ -1,11 +1,11 @@ -using System; +using System; using System.Reflection; namespace StardewModdingAPI.Framework.Reflection { - /// <summary>A private field obtained through reflection.</summary> + /// <summary>A field obtained through reflection.</summary> /// <typeparam name="TValue">The field value type.</typeparam> - internal class PrivateField<TValue> : IPrivateField<TValue> + internal class ReflectedField<TValue> : IPrivateField<TValue>, IReflectedField<TValue> { /********* ** Properties @@ -37,7 +37,7 @@ namespace StardewModdingAPI.Framework.Reflection /// <param name="isStatic">Whether the field is static.</param> /// <exception cref="ArgumentNullException">The <paramref name="parentType"/> or <paramref name="field"/> is null.</exception> /// <exception cref="ArgumentException">The <paramref name="obj"/> is null for a non-static field, or not null for a static field.</exception> - public PrivateField(Type parentType, object obj, FieldInfo field, bool isStatic) + public ReflectedField(Type parentType, object obj, FieldInfo field, bool isStatic) { // validate if (parentType == null) @@ -64,11 +64,11 @@ namespace StardewModdingAPI.Framework.Reflection } catch (InvalidCastException) { - throw new InvalidCastException($"Can't convert the private {this.DisplayName} field from {this.FieldInfo.FieldType.FullName} to {typeof(TValue).FullName}."); + throw new InvalidCastException($"Can't convert the {this.DisplayName} field from {this.FieldInfo.FieldType.FullName} to {typeof(TValue).FullName}."); } catch (Exception ex) { - throw new Exception($"Couldn't get the value of the private {this.DisplayName} field", ex); + throw new Exception($"Couldn't get the value of the {this.DisplayName} field", ex); } } @@ -82,11 +82,11 @@ namespace StardewModdingAPI.Framework.Reflection } catch (InvalidCastException) { - throw new InvalidCastException($"Can't assign the private {this.DisplayName} field a {typeof(TValue).FullName} value, must be compatible with {this.FieldInfo.FieldType.FullName}."); + throw new InvalidCastException($"Can't assign the {this.DisplayName} field a {typeof(TValue).FullName} value, must be compatible with {this.FieldInfo.FieldType.FullName}."); } catch (Exception ex) { - throw new Exception($"Couldn't set the value of the private {this.DisplayName} field", ex); + throw new Exception($"Couldn't set the value of the {this.DisplayName} field", ex); } } } diff --git a/src/SMAPI/Framework/Reflection/PrivateMethod.cs b/src/SMAPI/Framework/Reflection/ReflectedMethod.cs index ba2374f4..376de869 100644 --- a/src/SMAPI/Framework/Reflection/PrivateMethod.cs +++ b/src/SMAPI/Framework/Reflection/ReflectedMethod.cs @@ -3,8 +3,8 @@ using System.Reflection; namespace StardewModdingAPI.Framework.Reflection { - /// <summary>A private method obtained through reflection.</summary> - internal class PrivateMethod : IPrivateMethod + /// <summary>A method obtained through reflection.</summary> + internal class ReflectedMethod : IPrivateMethod, IReflectedMethod { /********* ** Properties @@ -33,10 +33,10 @@ namespace StardewModdingAPI.Framework.Reflection /// <param name="parentType">The type that has the method.</param> /// <param name="obj">The object that has the instance method(if applicable).</param> /// <param name="method">The reflection metadata.</param> - /// <param name="isStatic">Whether the field is static.</param> + /// <param name="isStatic">Whether the method is static.</param> /// <exception cref="ArgumentNullException">The <paramref name="parentType"/> or <paramref name="method"/> is null.</exception> /// <exception cref="ArgumentException">The <paramref name="obj"/> is null for a non-static method, or not null for a static method.</exception> - public PrivateMethod(Type parentType, object obj, MethodInfo method, bool isStatic) + public ReflectedMethod(Type parentType, object obj, MethodInfo method, bool isStatic) { // validate if (parentType == null) @@ -67,7 +67,7 @@ namespace StardewModdingAPI.Framework.Reflection } catch (Exception ex) { - throw new Exception($"Couldn't invoke the private {this.DisplayName} field", ex); + throw new Exception($"Couldn't invoke the {this.DisplayName} method", ex); } // cast return value @@ -77,7 +77,7 @@ namespace StardewModdingAPI.Framework.Reflection } catch (InvalidCastException) { - throw new InvalidCastException($"Can't convert the return value of the private {this.DisplayName} method from {this.MethodInfo.ReturnType.FullName} to {typeof(TValue).FullName}."); + throw new InvalidCastException($"Can't convert the return value of the {this.DisplayName} method from {this.MethodInfo.ReturnType.FullName} to {typeof(TValue).FullName}."); } } @@ -92,8 +92,8 @@ namespace StardewModdingAPI.Framework.Reflection } catch (Exception ex) { - throw new Exception($"Couldn't invoke the private {this.DisplayName} field", ex); + throw new Exception($"Couldn't invoke the {this.DisplayName} method", ex); } } } -}
\ No newline at end of file +} diff --git a/src/SMAPI/Framework/Reflection/PrivateProperty.cs b/src/SMAPI/Framework/Reflection/ReflectedProperty.cs index be346d71..d6c964c1 100644 --- a/src/SMAPI/Framework/Reflection/PrivateProperty.cs +++ b/src/SMAPI/Framework/Reflection/ReflectedProperty.cs @@ -3,9 +3,9 @@ using System.Reflection; namespace StardewModdingAPI.Framework.Reflection { - /// <summary>A private property obtained through reflection.</summary> + /// <summary>A property obtained through reflection.</summary> /// <typeparam name="TValue">The property value type.</typeparam> - internal class PrivateProperty<TValue> : IPrivateProperty<TValue> + internal class ReflectedProperty<TValue> : IPrivateProperty<TValue>, IReflectedProperty<TValue> { /********* ** Properties @@ -14,10 +14,10 @@ namespace StardewModdingAPI.Framework.Reflection private readonly string DisplayName; /// <summary>The underlying property getter.</summary> - private readonly Func<TValue> GetterDelegate; + private readonly Func<TValue> GetMethod; /// <summary>The underlying property setter.</summary> - private readonly Action<TValue> SetterDelegate; + private readonly Action<TValue> SetMethod; /********* @@ -31,13 +31,13 @@ namespace StardewModdingAPI.Framework.Reflection ** Public methods *********/ /// <summary>Construct an instance.</summary> - /// <param name="parentType">The type that has the field.</param> - /// <param name="obj">The object that has the instance field (if applicable).</param> + /// <param name="parentType">The type that has the property.</param> + /// <param name="obj">The object that has the instance property (if applicable).</param> /// <param name="property">The reflection metadata.</param> - /// <param name="isStatic">Whether the field is static.</param> + /// <param name="isStatic">Whether the property is static.</param> /// <exception cref="ArgumentNullException">The <paramref name="parentType"/> or <paramref name="property"/> is null.</exception> - /// <exception cref="ArgumentException">The <paramref name="obj"/> is null for a non-static field, or not null for a static field.</exception> - public PrivateProperty(Type parentType, object obj, PropertyInfo property, bool isStatic) + /// <exception cref="ArgumentException">The <paramref name="obj"/> is null for a non-static property, or not null for a static property.</exception> + public ReflectedProperty(Type parentType, object obj, PropertyInfo property, bool isStatic) { // validate input if (parentType == null) @@ -55,24 +55,29 @@ namespace StardewModdingAPI.Framework.Reflection this.DisplayName = $"{parentType.FullName}::{property.Name}"; this.PropertyInfo = property; - this.GetterDelegate = (Func<TValue>)Delegate.CreateDelegate(typeof(Func<TValue>), obj, this.PropertyInfo.GetMethod); - this.SetterDelegate = (Action<TValue>)Delegate.CreateDelegate(typeof(Action<TValue>), obj, this.PropertyInfo.SetMethod); + if (this.PropertyInfo.GetMethod != null) + this.GetMethod = (Func<TValue>)Delegate.CreateDelegate(typeof(Func<TValue>), obj, this.PropertyInfo.GetMethod); + if (this.PropertyInfo.SetMethod != null) + this.SetMethod = (Action<TValue>)Delegate.CreateDelegate(typeof(Action<TValue>), obj, this.PropertyInfo.SetMethod); } /// <summary>Get the property value.</summary> public TValue GetValue() { + if (this.GetMethod == null) + throw new InvalidOperationException($"The {this.DisplayName} property has no get method."); + try { - return this.GetterDelegate(); + return this.GetMethod(); } catch (InvalidCastException) { - throw new InvalidCastException($"Can't convert the private {this.DisplayName} property from {this.PropertyInfo.PropertyType.FullName} to {typeof(TValue).FullName}."); + throw new InvalidCastException($"Can't convert the {this.DisplayName} property from {this.PropertyInfo.PropertyType.FullName} to {typeof(TValue).FullName}."); } catch (Exception ex) { - throw new Exception($"Couldn't get the value of the private {this.DisplayName} property", ex); + throw new Exception($"Couldn't get the value of the {this.DisplayName} property", ex); } } @@ -80,17 +85,20 @@ namespace StardewModdingAPI.Framework.Reflection //// <param name="value">The value to set.</param> public void SetValue(TValue value) { + if (this.SetMethod == null) + throw new InvalidOperationException($"The {this.DisplayName} property has no set method."); + try { - this.SetterDelegate(value); + this.SetMethod(value); } catch (InvalidCastException) { - throw new InvalidCastException($"Can't assign the private {this.DisplayName} property a {typeof(TValue).FullName} value, must be compatible with {this.PropertyInfo.PropertyType.FullName}."); + throw new InvalidCastException($"Can't assign the {this.DisplayName} property a {typeof(TValue).FullName} value, must be compatible with {this.PropertyInfo.PropertyType.FullName}."); } catch (Exception ex) { - throw new Exception($"Couldn't set the value of the private {this.DisplayName} property", ex); + throw new Exception($"Couldn't set the value of the {this.DisplayName} property", ex); } } } diff --git a/src/SMAPI/Framework/Reflection/Reflector.cs b/src/SMAPI/Framework/Reflection/Reflector.cs index 23a48505..910e3a54 100644 --- a/src/SMAPI/Framework/Reflection/Reflector.cs +++ b/src/SMAPI/Framework/Reflection/Reflector.cs @@ -5,7 +5,7 @@ using System.Runtime.Caching; namespace StardewModdingAPI.Framework.Reflection { - /// <summary>Provides helper methods for accessing private game code.</summary> + /// <summary>Provides helper methods for accessing inaccessible code.</summary> /// <remarks>This implementation searches up the type hierarchy, and caches the reflected fields and methods with a sliding expiry (to optimise performance without unnecessary memory usage).</remarks> internal class Reflector { @@ -25,139 +25,139 @@ namespace StardewModdingAPI.Framework.Reflection /**** ** Fields ****/ - /// <summary>Get a private instance field.</summary> + /// <summary>Get a instance field.</summary> /// <typeparam name="TValue">The field type.</typeparam> /// <param name="obj">The object which has the field.</param> /// <param name="name">The field name.</param> - /// <param name="required">Whether to throw an exception if the private field is not found.</param> + /// <param name="required">Whether to throw an exception if the field is not found.</param> /// <returns>Returns the field wrapper, or <c>null</c> if the field doesn't exist and <paramref name="required"/> is <c>false</c>.</returns> - public IPrivateField<TValue> GetPrivateField<TValue>(object obj, string name, bool required = true) + public IReflectedField<TValue> GetField<TValue>(object obj, string name, bool required = true) { // validate if (obj == null) - throw new ArgumentNullException(nameof(obj), "Can't get a private instance field from a null object."); + throw new ArgumentNullException(nameof(obj), "Can't get a instance field from a null object."); // get field from hierarchy - IPrivateField<TValue> field = this.GetFieldFromHierarchy<TValue>(obj.GetType(), obj, name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + IReflectedField<TValue> field = this.GetFieldFromHierarchy<TValue>(obj.GetType(), obj, name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); if (required && field == null) - throw new InvalidOperationException($"The {obj.GetType().FullName} object doesn't have a private '{name}' instance field."); + throw new InvalidOperationException($"The {obj.GetType().FullName} object doesn't have a '{name}' instance field."); return field; } - /// <summary>Get a private static field.</summary> + /// <summary>Get a static field.</summary> /// <typeparam name="TValue">The field type.</typeparam> /// <param name="type">The type which has the field.</param> /// <param name="name">The field name.</param> - /// <param name="required">Whether to throw an exception if the private field is not found.</param> - public IPrivateField<TValue> GetPrivateField<TValue>(Type type, string name, bool required = true) + /// <param name="required">Whether to throw an exception if the field is not found.</param> + public IReflectedField<TValue> GetField<TValue>(Type type, string name, bool required = true) { // get field from hierarchy - IPrivateField<TValue> field = this.GetFieldFromHierarchy<TValue>(type, null, name, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Public); + IReflectedField<TValue> field = this.GetFieldFromHierarchy<TValue>(type, null, name, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Public); if (required && field == null) - throw new InvalidOperationException($"The {type.FullName} object doesn't have a private '{name}' static field."); + throw new InvalidOperationException($"The {type.FullName} object doesn't have a '{name}' static field."); return field; } /**** ** Properties ****/ - /// <summary>Get a private instance property.</summary> + /// <summary>Get a instance property.</summary> /// <typeparam name="TValue">The property type.</typeparam> /// <param name="obj">The object which has the property.</param> /// <param name="name">The property name.</param> - /// <param name="required">Whether to throw an exception if the private property is not found.</param> - public IPrivateProperty<TValue> GetPrivateProperty<TValue>(object obj, string name, bool required = true) + /// <param name="required">Whether to throw an exception if the property is not found.</param> + public IReflectedProperty<TValue> GetProperty<TValue>(object obj, string name, bool required = true) { // validate if (obj == null) - throw new ArgumentNullException(nameof(obj), "Can't get a private instance property from a null object."); + throw new ArgumentNullException(nameof(obj), "Can't get a instance property from a null object."); // get property from hierarchy - IPrivateProperty<TValue> property = this.GetPropertyFromHierarchy<TValue>(obj.GetType(), obj, name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + IReflectedProperty<TValue> property = this.GetPropertyFromHierarchy<TValue>(obj.GetType(), obj, name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); if (required && property == null) - throw new InvalidOperationException($"The {obj.GetType().FullName} object doesn't have a private '{name}' instance property."); + throw new InvalidOperationException($"The {obj.GetType().FullName} object doesn't have a '{name}' instance property."); return property; } - /// <summary>Get a private static property.</summary> + /// <summary>Get a static property.</summary> /// <typeparam name="TValue">The property type.</typeparam> /// <param name="type">The type which has the property.</param> /// <param name="name">The property name.</param> - /// <param name="required">Whether to throw an exception if the private property is not found.</param> - public IPrivateProperty<TValue> GetPrivateProperty<TValue>(Type type, string name, bool required = true) + /// <param name="required">Whether to throw an exception if the property is not found.</param> + public IReflectedProperty<TValue> GetProperty<TValue>(Type type, string name, bool required = true) { // get field from hierarchy - IPrivateProperty<TValue> property = this.GetPropertyFromHierarchy<TValue>(type, null, name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); + IReflectedProperty<TValue> property = this.GetPropertyFromHierarchy<TValue>(type, null, name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); if (required && property == null) - throw new InvalidOperationException($"The {type.FullName} object doesn't have a private '{name}' static property."); + throw new InvalidOperationException($"The {type.FullName} object doesn't have a '{name}' static property."); return property; } /**** ** Methods ****/ - /// <summary>Get a private instance method.</summary> + /// <summary>Get a instance method.</summary> /// <param name="obj">The object which has the method.</param> /// <param name="name">The field name.</param> - /// <param name="required">Whether to throw an exception if the private field is not found.</param> - public IPrivateMethod GetPrivateMethod(object obj, string name, bool required = true) + /// <param name="required">Whether to throw an exception if the field is not found.</param> + public IReflectedMethod GetMethod(object obj, string name, bool required = true) { // validate if (obj == null) - throw new ArgumentNullException(nameof(obj), "Can't get a private instance method from a null object."); + throw new ArgumentNullException(nameof(obj), "Can't get a instance method from a null object."); // get method from hierarchy - IPrivateMethod method = this.GetMethodFromHierarchy(obj.GetType(), obj, name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + IReflectedMethod method = this.GetMethodFromHierarchy(obj.GetType(), obj, name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); if (required && method == null) - throw new InvalidOperationException($"The {obj.GetType().FullName} object doesn't have a private '{name}' instance method."); + throw new InvalidOperationException($"The {obj.GetType().FullName} object doesn't have a '{name}' instance method."); return method; } - /// <summary>Get a private static method.</summary> + /// <summary>Get a static method.</summary> /// <param name="type">The type which has the method.</param> /// <param name="name">The field name.</param> - /// <param name="required">Whether to throw an exception if the private field is not found.</param> - public IPrivateMethod GetPrivateMethod(Type type, string name, bool required = true) + /// <param name="required">Whether to throw an exception if the field is not found.</param> + public IReflectedMethod GetMethod(Type type, string name, bool required = true) { // get method from hierarchy - IPrivateMethod method = this.GetMethodFromHierarchy(type, null, name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); + IReflectedMethod method = this.GetMethodFromHierarchy(type, null, name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); if (required && method == null) - throw new InvalidOperationException($"The {type.FullName} object doesn't have a private '{name}' static method."); + throw new InvalidOperationException($"The {type.FullName} object doesn't have a '{name}' static method."); return method; } /**** ** Methods by signature ****/ - /// <summary>Get a private instance method.</summary> + /// <summary>Get a instance method.</summary> /// <param name="obj">The object which has the method.</param> /// <param name="name">The field name.</param> /// <param name="argumentTypes">The argument types of the method signature to find.</param> - /// <param name="required">Whether to throw an exception if the private field is not found.</param> - public IPrivateMethod GetPrivateMethod(object obj, string name, Type[] argumentTypes, bool required = true) + /// <param name="required">Whether to throw an exception if the field is not found.</param> + public IReflectedMethod GetMethod(object obj, string name, Type[] argumentTypes, bool required = true) { // validate parent if (obj == null) - throw new ArgumentNullException(nameof(obj), "Can't get a private instance method from a null object."); + throw new ArgumentNullException(nameof(obj), "Can't get a instance method from a null object."); // get method from hierarchy - PrivateMethod method = this.GetMethodFromHierarchy(obj.GetType(), obj, name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, argumentTypes); + ReflectedMethod method = this.GetMethodFromHierarchy(obj.GetType(), obj, name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, argumentTypes); if (required && method == null) - throw new InvalidOperationException($"The {obj.GetType().FullName} object doesn't have a private '{name}' instance method with that signature."); + throw new InvalidOperationException($"The {obj.GetType().FullName} object doesn't have a '{name}' instance method with that signature."); return method; } - /// <summary>Get a private static method.</summary> + /// <summary>Get a static method.</summary> /// <param name="type">The type which has the method.</param> /// <param name="name">The field name.</param> /// <param name="argumentTypes">The argument types of the method signature to find.</param> - /// <param name="required">Whether to throw an exception if the private field is not found.</param> - public IPrivateMethod GetPrivateMethod(Type type, string name, Type[] argumentTypes, bool required = true) + /// <param name="required">Whether to throw an exception if the field is not found.</param> + public IReflectedMethod GetMethod(Type type, string name, Type[] argumentTypes, bool required = true) { // get field from hierarchy - PrivateMethod method = this.GetMethodFromHierarchy(type, null, name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, argumentTypes); + ReflectedMethod method = this.GetMethodFromHierarchy(type, null, name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, argumentTypes); if (required && method == null) - throw new InvalidOperationException($"The {type.FullName} object doesn't have a private '{name}' static method with that signature."); + throw new InvalidOperationException($"The {type.FullName} object doesn't have a '{name}' static method with that signature."); return method; } @@ -171,7 +171,7 @@ namespace StardewModdingAPI.Framework.Reflection /// <param name="obj">The object which has the field.</param> /// <param name="name">The field name.</param> /// <param name="bindingFlags">The reflection binding which flags which indicates what type of field to find.</param> - private IPrivateField<TValue> GetFieldFromHierarchy<TValue>(Type type, object obj, string name, BindingFlags bindingFlags) + private IReflectedField<TValue> GetFieldFromHierarchy<TValue>(Type type, object obj, string name, BindingFlags bindingFlags) { bool isStatic = bindingFlags.HasFlag(BindingFlags.Static); FieldInfo field = this.GetCached<FieldInfo>($"field::{isStatic}::{type.FullName}::{name}", () => @@ -183,7 +183,7 @@ namespace StardewModdingAPI.Framework.Reflection }); return field != null - ? new PrivateField<TValue>(type, obj, field, isStatic) + ? new ReflectedField<TValue>(type, obj, field, isStatic) : null; } @@ -193,7 +193,7 @@ namespace StardewModdingAPI.Framework.Reflection /// <param name="obj">The object which has the property.</param> /// <param name="name">The property name.</param> /// <param name="bindingFlags">The reflection binding which flags which indicates what type of property to find.</param> - private IPrivateProperty<TValue> GetPropertyFromHierarchy<TValue>(Type type, object obj, string name, BindingFlags bindingFlags) + private IReflectedProperty<TValue> GetPropertyFromHierarchy<TValue>(Type type, object obj, string name, BindingFlags bindingFlags) { bool isStatic = bindingFlags.HasFlag(BindingFlags.Static); PropertyInfo property = this.GetCached<PropertyInfo>($"property::{isStatic}::{type.FullName}::{name}", () => @@ -205,7 +205,7 @@ namespace StardewModdingAPI.Framework.Reflection }); return property != null - ? new PrivateProperty<TValue>(type, obj, property, isStatic) + ? new ReflectedProperty<TValue>(type, obj, property, isStatic) : null; } @@ -214,7 +214,7 @@ namespace StardewModdingAPI.Framework.Reflection /// <param name="obj">The object which has the method.</param> /// <param name="name">The method name.</param> /// <param name="bindingFlags">The reflection binding which flags which indicates what type of method to find.</param> - private IPrivateMethod GetMethodFromHierarchy(Type type, object obj, string name, BindingFlags bindingFlags) + private IReflectedMethod GetMethodFromHierarchy(Type type, object obj, string name, BindingFlags bindingFlags) { bool isStatic = bindingFlags.HasFlag(BindingFlags.Static); MethodInfo method = this.GetCached($"method::{isStatic}::{type.FullName}::{name}", () => @@ -226,7 +226,7 @@ namespace StardewModdingAPI.Framework.Reflection }); return method != null - ? new PrivateMethod(type, obj, method, isStatic: bindingFlags.HasFlag(BindingFlags.Static)) + ? new ReflectedMethod(type, obj, method, isStatic: bindingFlags.HasFlag(BindingFlags.Static)) : null; } @@ -236,7 +236,7 @@ namespace StardewModdingAPI.Framework.Reflection /// <param name="name">The method name.</param> /// <param name="bindingFlags">The reflection binding which flags which indicates what type of method to find.</param> /// <param name="argumentTypes">The argument types of the method signature to find.</param> - private PrivateMethod GetMethodFromHierarchy(Type type, object obj, string name, BindingFlags bindingFlags, Type[] argumentTypes) + private ReflectedMethod GetMethodFromHierarchy(Type type, object obj, string name, BindingFlags bindingFlags, Type[] argumentTypes) { bool isStatic = bindingFlags.HasFlag(BindingFlags.Static); MethodInfo method = this.GetCached($"method::{isStatic}::{type.FullName}::{name}({string.Join(",", argumentTypes.Select(p => p.FullName))})", () => @@ -247,7 +247,7 @@ namespace StardewModdingAPI.Framework.Reflection return methodInfo; }); return method != null - ? new PrivateMethod(type, obj, method, isStatic) + ? new ReflectedMethod(type, obj, method, isStatic) : null; } |