yasirkula / UnityIngameDebugConsole

A uGUI based console to see debug messages and execute commands during gameplay in Unity
MIT License
2.05k stars 217 forks source link

[feature request] support extension method #95

Open kirsysuv opened 2 weeks ago

kirsysuv commented 2 weeks ago

I want to add some extension method such as

public static class ClassAExtension
{
        public static void test22(this ClassA self)
        {
              //
        }
}

I tried to use a fixed parameter name "self" to identify the "this" parameter in extension method. And some other workarounds to make extension method usable. My implementation is not well organized and tested.

It would be great if this feature is implemented.

Here is my workaroud

==========================
    public class ConsoleMethodInfo
    {
        public readonly MethodInfo method;
        public readonly Type[] parameterTypes;
        public readonly object instance;

        public readonly string command;
        public readonly string signature;
        public readonly string[] parameters;
                // specify extension method
        public readonly bool isExtensionMethod;

        public ConsoleMethodInfo( MethodInfo method, Type[] parameterTypes, object instance, string command, string signature, string[] parameters )
        {
            this.method = method;
            this.parameterTypes = parameterTypes;
            this.instance = instance;
            this.command = command;
            this.signature = signature;
            this.parameters = parameters;
            this.isExtensionMethod = false;
        }

        public ConsoleMethodInfo( MethodInfo method, Type[] parameterTypes, object instance, string command, string signature, string[] parameters, bool isExtensionMethod )
        {
                        this.method = method;
                        this.parameterTypes = parameterTypes;
                        this.instance = instance;
                        this.command = command;
                        this.signature = signature;
                        this.parameters = parameters;
                        this.isExtensionMethod = isExtensionMethod;
        }

        public bool IsValid()
        {
            if( !method.IsStatic && ( instance == null || instance.Equals( null ) ) )
                return false;

            return true;
        }
    }
=========================
        // Add a command related with a static extension method
        public static void AddCommandExtension(string command, string description, string methodName, Type extensionClassType, object instance, params string[] parameterNames)
        {
            // Get the method from the class
            MethodInfo method = extensionClassType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);
            if (method == null)
            {
                Debug.LogError(methodName + " does not exist in " + extensionClassType);
                return;
            }

            AddCommand(command, description, method, instance, parameterNames);
        }

========================= modify AddCommand method
...
            // Fetch the parameters of the class
            ParameterInfo[] parameters = method.GetParameters();
            if( parameters == null )
                parameters = new ParameterInfo[0];

            bool isExtensionMethod = false;
            if (parameters.Length>0 && parameters[0].Name == "self")
            {
                isExtensionMethod = true;
                //remove the first parameter in parameters

                //in the case of extension method, the first parameter is the instance of the class
                //remove it from the parameters list and check if there are any parameters left
                if (parameters.Length > 1)
                {
                    ParameterInfo[] newParameters = new ParameterInfo[parameters.Length - 1];
                    for (int i = 1; i < parameters.Length; i++)
                    {
                        newParameters[i - 1] = parameters[i];
                    }
                    parameters = newParameters;
                }
                else
                {
                    parameters = new ParameterInfo[0];
                }

            }

            // Store the parameter types in an array
            Type[] parameterTypes = new Type[parameters.Length];
...

            methods.Insert( commandIndex, new ConsoleMethodInfo( method, parameterTypes, instance, command, methodSignature.ToString(), parameterSignatures, isExtensionMethod ) );

========================= modify ExecuteCommand method
...

...
            if( methodToExecute == null )
                Debug.LogWarning( !string.IsNullOrEmpty( errorMessage ) ? errorMessage : "ERROR: something went wrong" );
            else
            {
                object result;
                // Execute the method associated with the command
                if( methodToExecute.isExtensionMethod )
                {
                    // If the method is an extension method, the first parameter should be the instance
                    object[] newParameters = new object[parameters.Length + 1];
                                        // use instance as first parameter
                    newParameters[0] = methodToExecute.instance;
                    Array.Copy( parameters, 0, newParameters, 1, parameters.Length );
                    parameters = newParameters;
                    result = methodToExecute.method.Invoke(null, parameters);
                }
                else
                {
                    result = methodToExecute.method.Invoke( methodToExecute.instance, parameters );

                }
yasirkula commented 2 weeks ago

I see. I'd workaround this limitation as follows:

ClassA instance = ...;
DebugLogConsole.AddCommand("name", "desc", (string parameter1, int parameter2) => instance.test22(parameter1, parameter2));

In the future, I may add better support similar to yours but at the moment, I'm unable to.

kirsysuv commented 2 weeks ago

Wow! This workaround is great! Thank you for the reply.