using System;
using System.Reflection;
using StardewModdingAPI.Reflection;
namespace StardewModdingAPI.Framework.Reflection
{
/// Provides helper methods for accessing private game code.
internal class ReflectionHelper : IReflectionHelper
{
/*********
** Public methods
*********/
/****
** Fields
****/
/// Get a private instance field.
/// The field type.
/// The object which has the field.
/// The field name.
/// Whether to throw an exception if the private field is not found.
/// Returns the field wrapper, or null if the field doesn't exist and is false.
public IPrivateField GetPrivateField(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.");
// get field from hierarchy
IPrivateField field = this.GetFieldFromHierarchy(obj.GetType(), obj, name, BindingFlags.Instance | BindingFlags.NonPublic);
if (required && field == null)
throw new InvalidOperationException($"The {obj.GetType().FullName} object doesn't have a private '{name}' instance field.");
return field;
}
/// Get a private static field.
/// The field type.
/// The type which has the field.
/// The field name.
/// Whether to throw an exception if the private field is not found.
public IPrivateField GetPrivateField(Type type, string name, bool required = true)
{
// get field from hierarchy
IPrivateField field = this.GetFieldFromHierarchy(type, null, name, BindingFlags.NonPublic | BindingFlags.Static);
if (required && field == null)
throw new InvalidOperationException($"The {type.FullName} object doesn't have a private '{name}' static field.");
return field;
}
/****
** Field values
** (shorthand since this is the most common case)
****/
/// Get the value of a private instance field.
/// The field type.
/// The object which has the field.
/// The field name.
/// Whether to throw an exception if the private field is not found.
/// This is a shortcut for followed by .
public TValue GetPrivateValue(object obj, string name, bool required = true)
{
return this.GetPrivateField(obj, name, required).GetValue();
}
/// Get the value of a private static field.
/// The field type.
/// The type which has the field.
/// The field name.
/// Whether to throw an exception if the private field is not found.
/// This is a shortcut for followed by .
public TValue GetPrivateValue(Type type, string name, bool required = true)
{
return this.GetPrivateField(type, name, required).GetValue();
}
/****
** Methods
****/
/// Get a private instance method.
/// The object which has the method.
/// The field name.
/// Whether to throw an exception if the private field is not found.
public IPrivateMethod GetPrivateMethod(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.");
// get method from hierarchy
IPrivateMethod method = this.GetMethodFromHierarchy(obj.GetType(), obj, name, BindingFlags.Instance | BindingFlags.NonPublic);
if (required && method == null)
throw new InvalidOperationException($"The {obj.GetType().FullName} object doesn't have a private '{name}' instance method.");
return method;
}
/// Get a private static method.
/// The type which has the method.
/// The field name.
/// Whether to throw an exception if the private field is not found.
public IPrivateMethod GetPrivateMethod(Type type, string name, bool required = true)
{
// get method from hierarchy
IPrivateMethod method = this.GetMethodFromHierarchy(type, null, name, BindingFlags.NonPublic | BindingFlags.Static);
if (required && method == null)
throw new InvalidOperationException($"The {type.FullName} object doesn't have a private '{name}' static method.");
return method;
}
/****
** Methods by signature
****/
/// Get a private instance method.
/// The object which has the method.
/// The field name.
/// The argument types of the method signature to find.
/// Whether to throw an exception if the private field is not found.
public IPrivateMethod GetPrivateMethod(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.");
// get method from hierarchy
PrivateMethod method = this.GetMethodFromHierarchy(obj.GetType(), obj, name, BindingFlags.Instance | BindingFlags.NonPublic, argumentTypes);
if (required && method == null)
throw new InvalidOperationException($"The {obj.GetType().FullName} object doesn't have a private '{name}' instance method with that signature.");
return method;
}
/// Get a private static method.
/// The type which has the method.
/// The field name.
/// The argument types of the method signature to find.
/// Whether to throw an exception if the private field is not found.
public IPrivateMethod GetPrivateMethod(Type type, string name, Type[] argumentTypes, bool required = true)
{
// get field from hierarchy
PrivateMethod method = this.GetMethodFromHierarchy(type, null, name, BindingFlags.NonPublic | 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.");
return method;
}
/*********
** Private methods
*********/
/// Get a field from the type hierarchy.
/// The expected field type.
/// The type which has the field.
/// The object which has the field.
/// The field name.
/// The reflection binding which flags which indicates what type of field to find.
private IPrivateField GetFieldFromHierarchy(Type type, object obj, string name, BindingFlags bindingFlags)
{
FieldInfo field = null;
for (; type != null && field == null; type = type.BaseType)
field = type.GetField(name, bindingFlags);
return field != null
? new PrivateField(type, obj, field, isStatic: bindingFlags.HasFlag(BindingFlags.Static))
: null;
}
/// Get a method from the type hierarchy.
/// The type which has the method.
/// The object which has the method.
/// The method name.
/// The reflection binding which flags which indicates what type of method to find.
private IPrivateMethod GetMethodFromHierarchy(Type type, object obj, string name, BindingFlags bindingFlags)
{
MethodInfo method = null;
for (; type != null && method == null; type = type.BaseType)
method = type.GetMethod(name, bindingFlags);
return method != null
? new PrivateMethod(type, obj, method, isStatic: bindingFlags.HasFlag(BindingFlags.Static))
: null;
}
/// Get a method from the type hierarchy.
/// The type which has the method.
/// The object which has the method.
/// The method name.
/// The reflection binding which flags which indicates what type of method to find.
/// The argument types of the method signature to find.
private PrivateMethod GetMethodFromHierarchy(Type type, object obj, string name, BindingFlags bindingFlags, Type[] argumentTypes)
{
MethodInfo method = null;
for (; type != null && method == null; type = type.BaseType)
method = type.GetMethod(name, bindingFlags, null, argumentTypes, null);
return method != null
? new PrivateMethod(type, obj, method, isStatic: bindingFlags.HasFlag(BindingFlags.Static))
: null;
}
}
}