157 lines
7.1 KiB
C#
157 lines
7.1 KiB
C#
![]() |
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using System.Reflection;
|
|||
|
using System.Reflection.Emit;
|
|||
|
using System.Text;
|
|||
|
using System.Threading.Tasks;
|
|||
|
|
|||
|
namespace astealz.SmartSpawnController.Utils
|
|||
|
{
|
|||
|
public delegate T PropertyGetter<out T>(object instance);
|
|||
|
public delegate void PropertySetter<in T>(object instance, T value);
|
|||
|
public delegate T MethodInvoker<in T1, out T>(object instance, T1 arg1);
|
|||
|
public delegate void MethodInvoker<in T>(object instance, T value);
|
|||
|
|
|||
|
// methods in this class creates a dynamic methods (and return a delegate) for quick access to a property or method which cannot be called by a direct call
|
|||
|
// this approach is about 5x faster than reflection
|
|||
|
static class Emit
|
|||
|
{
|
|||
|
// delegate for property getter
|
|||
|
public static PropertyGetter<T> CreateDynamicPropertyGetter<T>(
|
|||
|
PropertyInfo propertyInfo,
|
|||
|
BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)
|
|||
|
{
|
|||
|
if (propertyInfo == null)
|
|||
|
throw new ArgumentNullException(nameof(propertyInfo));
|
|||
|
|
|||
|
if (!typeof(T).IsAssignableFrom(propertyInfo.PropertyType))
|
|||
|
throw new ArgumentException($"Property type '{propertyInfo.PropertyType}' does not match return type '{typeof(T)}'");
|
|||
|
|
|||
|
var getterMethodInfo = propertyInfo.GetGetMethod(true);
|
|||
|
if (getterMethodInfo == null)
|
|||
|
throw new InvalidOperationException($"Can't find getter for property '{propertyInfo.Name}' in '{propertyInfo.DeclaringType}'");
|
|||
|
|
|||
|
var dynMethod = new DynamicMethod($"__get_{typeof(T).Name}_prop_{propertyInfo.Name}", typeof(T), new[] { typeof(object) }, propertyInfo.DeclaringType, true);
|
|||
|
var ilGen = dynMethod.GetILGenerator();
|
|||
|
if (getterMethodInfo.IsStatic)
|
|||
|
{
|
|||
|
ilGen.Emit(OpCodes.Call, getterMethodInfo);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ilGen.Emit(OpCodes.Ldarg_0);
|
|||
|
ilGen.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
|
|||
|
ilGen.Emit(OpCodes.Callvirt, getterMethodInfo);
|
|||
|
}
|
|||
|
ilGen.Emit(OpCodes.Ret);
|
|||
|
return (PropertyGetter<T>)dynMethod.CreateDelegate(typeof(PropertyGetter<T>));
|
|||
|
}
|
|||
|
|
|||
|
// delegate for property setter
|
|||
|
public static PropertySetter<T> CreateDynamicPropertySetter<T>(
|
|||
|
PropertyInfo propertyInfo,
|
|||
|
BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)
|
|||
|
{
|
|||
|
if (propertyInfo == null)
|
|||
|
throw new ArgumentNullException(nameof(propertyInfo));
|
|||
|
|
|||
|
if (!typeof(T).IsAssignableFrom(propertyInfo.PropertyType))
|
|||
|
throw new ArgumentException($"Property type '{propertyInfo.PropertyType}' does not match the type of value '{typeof(T)}'");
|
|||
|
|
|||
|
var setterMethodInfo = propertyInfo.GetSetMethod(true);
|
|||
|
if (setterMethodInfo == null)
|
|||
|
throw new InvalidOperationException($"Can't find setter for property '{propertyInfo.Name}' in '{propertyInfo.DeclaringType}'");
|
|||
|
|
|||
|
var dynMethod = new DynamicMethod($"__set_{typeof(T).Name}_prop_{propertyInfo.Name}", null, new[] { typeof(object), typeof(T) }, propertyInfo.DeclaringType);
|
|||
|
var ilGen = dynMethod.GetILGenerator();
|
|||
|
if (setterMethodInfo.IsStatic)
|
|||
|
{
|
|||
|
ilGen.Emit(OpCodes.Ldarg_1);
|
|||
|
ilGen.Emit(OpCodes.Call, setterMethodInfo);
|
|||
|
ilGen.Emit(OpCodes.Ret);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ilGen.Emit(OpCodes.Ldarg_0);
|
|||
|
ilGen.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
|
|||
|
ilGen.Emit(OpCodes.Ldarg_1);
|
|||
|
ilGen.Emit(OpCodes.Callvirt, setterMethodInfo);
|
|||
|
}
|
|||
|
ilGen.Emit(OpCodes.Ret);
|
|||
|
return (PropertySetter<T>)dynMethod.CreateDelegate(typeof(PropertySetter<T>));
|
|||
|
}
|
|||
|
|
|||
|
// delegate to invoke a method with one argument and return value
|
|||
|
public static MethodInvoker<T1, TR> CreateDynamicMethodInvoker<T1, TR>(MethodInfo methodInfo)
|
|||
|
{
|
|||
|
if (methodInfo == null)
|
|||
|
throw new ArgumentNullException(nameof(methodInfo));
|
|||
|
|
|||
|
if (!typeof(TR).IsAssignableFrom(methodInfo.ReturnType))
|
|||
|
throw new ArgumentException($"Method return type '{methodInfo.ReturnType}' does not match return type '{typeof(TR)}'");
|
|||
|
|
|||
|
var dynMethod = new DynamicMethod(
|
|||
|
$"__invoke_{typeof(TR).Name}_method_{methodInfo.Name}", // method name is only for debugging
|
|||
|
typeof(TR),
|
|||
|
new[] { typeof(object), typeof(T1) }, // add method arguments here (T2, T3, T4...)
|
|||
|
methodInfo.DeclaringType,
|
|||
|
true);
|
|||
|
|
|||
|
var ilGen = dynMethod.GetILGenerator();
|
|||
|
if (methodInfo.IsStatic)
|
|||
|
{
|
|||
|
// if target method is static push first argument on stack
|
|||
|
ilGen.Emit(OpCodes.Ldarg_1);
|
|||
|
ilGen.Emit(OpCodes.Call, methodInfo);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// otherwise push instance and cast it to type where method is declared
|
|||
|
ilGen.Emit(OpCodes.Ldarg_0);
|
|||
|
ilGen.Emit(OpCodes.Castclass, methodInfo.DeclaringType);
|
|||
|
// then push method arguments
|
|||
|
ilGen.Emit(OpCodes.Ldarg_1);
|
|||
|
ilGen.Emit(OpCodes.Callvirt, methodInfo);
|
|||
|
}
|
|||
|
ilGen.Emit(OpCodes.Ret);
|
|||
|
return (MethodInvoker<T1, TR>)dynMethod.CreateDelegate(typeof(MethodInvoker<T1, TR>));
|
|||
|
}
|
|||
|
|
|||
|
public static MethodInvoker<T> CreateDynamicMethodInvoker<T>(MethodInfo methodInfo)
|
|||
|
{
|
|||
|
if (methodInfo == null)
|
|||
|
throw new ArgumentNullException(nameof(methodInfo));
|
|||
|
|
|||
|
if (!typeof(void).Equals(methodInfo.ReturnType))
|
|||
|
throw new ArgumentException($"Method return type '{methodInfo.ReturnType}' does not match return type 'void'");
|
|||
|
|
|||
|
var dynMethod = new DynamicMethod(
|
|||
|
$"__invoke_void_method_{methodInfo.Name}", // method name is only for debugging
|
|||
|
typeof(void),
|
|||
|
new[] { typeof(object), typeof(T) }, // add method arguments here (T2, T3, T4...)
|
|||
|
methodInfo.DeclaringType,
|
|||
|
true);
|
|||
|
|
|||
|
var ilGen = dynMethod.GetILGenerator();
|
|||
|
if (methodInfo.IsStatic)
|
|||
|
{
|
|||
|
// if target method is static push first argument on stack
|
|||
|
ilGen.Emit(OpCodes.Ldarg_1);
|
|||
|
ilGen.Emit(OpCodes.Call, methodInfo);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// otherwise push instance and cast it to type where method is declared
|
|||
|
ilGen.Emit(OpCodes.Ldarg_0);
|
|||
|
ilGen.Emit(OpCodes.Castclass, methodInfo.DeclaringType);
|
|||
|
// then push method arguments
|
|||
|
ilGen.Emit(OpCodes.Ldarg_1);
|
|||
|
ilGen.Emit(OpCodes.Callvirt, methodInfo);
|
|||
|
}
|
|||
|
ilGen.Emit(OpCodes.Ret);
|
|||
|
return (MethodInvoker<T>)dynMethod.CreateDelegate(typeof(MethodInvoker<T>));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|