using System;
using System.Reflection;
using System.Reflection.Emit;
using UnityEngine;
namespace IPA.Utilities
{
///
/// A utility class providing reflection helper methods.
///
public static partial class ReflectionUtil
{
internal static readonly FieldInfo DynamicMethodReturnType =
typeof(DynamicMethod).GetField("returnType", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
///
/// Sets a field on the target object, as gotten from .
///
/// the type to get the field from
/// the type of the field to set
/// the object instance
/// the field to set
/// the value to set it to
/// if does not exist on
///
public static void SetField(this T obj, string fieldName, U value)
=> FieldAccessor.Set(ref obj, fieldName, value);
///
/// Gets the value of a field.
///
/// the type to get the field from
/// the type of the field (result casted)
/// the object instance to pull from
/// the name of the field to read
/// the value of the field
/// if does not exist on
///
public static U GetField(this T obj, string fieldName)
=> FieldAccessor.Get(ref obj, fieldName);
///
/// Sets a property on the target object, as gotten from .
///
/// the type to get the property from
/// the type of the property to set
/// the object instance
/// the property to set
/// the value to set it to
/// if does not exist on
///
public static void SetProperty(this T obj, string propertyName, U value)
=> PropertyAccessor.Set(ref obj, propertyName, value);
///
/// Gets a property on the target object, as gotten from .
///
/// the type to get the property from
/// the type of the property to get
/// the object instance
/// the property to get
/// the value of the property
/// if does not exist on
///
public static U GetProperty(this T obj, string propertyName)
=> PropertyAccessor.Get(ref obj, propertyName);
///
/// Invokes a method from on an object.
///
/// the type that the method returns
/// the type to search for the method on
/// the object instance
/// the method's name
/// the method arguments
/// the return value
/// if does not exist on
public static U InvokeMethod(this T obj, string methodName, params object[] args)
{
var dynMethod = typeof(T).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
if (dynMethod == null) throw new MissingMethodException($"Method {methodName} does not exist", nameof(methodName));
return (U)dynMethod?.Invoke(obj, args);
}
///
/// Copies a component to a component of on the destination .
///
/// the original component
/// the new component's type
/// the destination GameObject
/// overrides the source component type (for example, to a superclass)
/// the copied component
public static Component CopyComponent(this Component original, Type overridingType, GameObject destination, Type originalTypeOverride = null)
{
var copy = destination.AddComponent(overridingType);
var originalType = originalTypeOverride ?? original.GetType();
Type type = originalType;
while (type != typeof(MonoBehaviour))
{
CopyForType(type, original, copy);
type = type?.BaseType;
}
return copy;
}
///
/// A generic version of .
///
///
/// the overriding type
/// the original component
/// the destination game object
/// overrides the source component type (for example, to a superclass)
/// the copied component
public static T CopyComponent(this Component original, GameObject destination, Type originalTypeOverride = null)
where T : Component
{
var copy = destination.AddComponent();
var originalType = originalTypeOverride ?? original.GetType();
Type type = originalType;
while (type != typeof(MonoBehaviour))
{
CopyForType(type, original, copy);
type = type?.BaseType;
}
return copy;
}
///
/// Converts the property name to the one of the compiler-generated backing field.
/// This can be used for the field-based reflection when you want to set the value of a get-only property
///
/// Name of the property
/// Name of the backing field
///
/// Only works for properties with compiler-generated backing fields.
/// This is only a simple method and doesn't have any guarantees to work 100% of the time across different compilers/runtimes.
/// See this link for more info.
///
public static string ToCompilerGeneratedBackingField(string propertyName) => $"<{propertyName}>k__BackingField";
private static void CopyForType(Type type, Component source, Component destination)
{
FieldInfo[] myObjectFields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo fi in myObjectFields)
{
fi.SetValue(destination, fi.GetValue(source));
}
}
}
}