using System; using System.Linq; using System.Reflection; using UnityEngine; namespace SerializableFunc { [Serializable] public abstract class SerializableFuncBase where TFuncType : Delegate { [SerializeField] protected UnityEngine.Object targetObject; [SerializeField] protected string methodName; private TFuncType func; private static BindingFlags SuitableMethodsFlags = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance; public UnityEngine.Object TargetObject { get => targetObject; set => targetObject = value; } public string MethodName { get => methodName; set => methodName = value; } protected TFuncType GetReturnedFunc() { if (targetObject == null || string.IsNullOrWhiteSpace(methodName)) return null; if (func == null) { Type funcType = typeof(TFuncType); MethodInfo info = targetObject .GetType() .GetMethods(SuitableMethodsFlags) .FirstOrDefault(x => IsTargetMethodInfo(x, funcType)); if (info == null) { throw new MissingMethodException( $"Object \"{targetObject.name}\" is missing target method: {methodName}" ); } func = (TFuncType)Delegate.CreateDelegate(funcType, targetObject, methodName); } return func; } #region Utility Functions private bool IsTargetMethodInfo(MethodInfo methodInfo, Type funcType) { if (!string.Equals(methodInfo.Name, methodName, StringComparison.InvariantCulture)) return false; Type[] typeArguments = funcType.GetGenericArguments(); if (methodInfo.ReturnType != typeArguments.Last()) return false; ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length != (typeArguments.Length - 1)) return false; for (int i = 0; i < parameters.Length; i++) { Type argType = typeArguments[i]; ParameterInfo parameterInfo = parameters[i]; if (argType != parameterInfo.ParameterType) return false; } return true; } #endregion public static implicit operator TFuncType(SerializableFuncBase func) { if (func == null) return null; TFuncType result = func.GetReturnedFunc(); return result; } } }