Skip to content

Instantly share code, notes, and snippets.

@muhammetozeski
Created April 27, 2024 20:12
Show Gist options
  • Save muhammetozeski/651ce2c963989fc301488131d21fb676 to your computer and use it in GitHub Desktop.
Save muhammetozeski/651ce2c963989fc301488131d21fb676 to your computer and use it in GitHub Desktop.
using System.Reflection;
/// <summary>
/// This class is for calling a function from a class with string inputs. Example usage:
///<code>
///Console.WriteLine("You can call these functions:");
///string[] functions = CallFunctions.GetMethodsAsStrings(typeof(TestClass));
///for (int i = 0; i &lt; functions.Length; i++) Console.WriteLine(functions[i]);
///Console.WriteLine("Write command:");
///string input = Console.ReadLine();
///bool result = CallFunctions.Call(typeof(TestClass), input);
///if (result) p("method is called succesfully");
///else p("method couldn't be called");
///</code>
/// </summary>
public static class CallFunctions
{
public static bool PrintDebugMessages = true;
static void DefaultDebugPrintFunction(object message)
{
if (!PrintDebugMessages)
return;
if (message == null)
{
Console.WriteLine("null");
return;
}
Console.WriteLine(message.ToString());
}
/// <summary>
/// default print function for this class. it prints using Console.WriteLine()
/// you can change it if you are using a different platform such as unity. for example change it to MonoBehavior.Print function if you are working in unity.
/// </summary>
public static Action<object> DebugPrint = DefaultDebugPrintFunction;
/// <summary>
/// gets the methods with their parameters. only the methods will be returned that matches with this call:<br></br> type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
/// </summary>
/// <param name="type">The class that will be scanned</param>
/// <returns></returns>
public static string[] GetMethodsAsStrings(Type type)
{
MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
List<string> MethodsAsStrings = new();
foreach (MethodInfo method in methods)
{
ParameterInfo[] parameters = method.GetParameters();
string methodSignature = method.Name + "(";
for (int i = 0; i < parameters.Length; i++)
{
string paramName = parameters[i].Name;
string paramType = parameters[i].ParameterType.Name;
string defaultValue = parameters[i].HasDefaultValue ? " = " + parameters[i].DefaultValue : "";
methodSignature += paramType + " " + paramName + defaultValue;
if (i < parameters.Length - 1)
{
methodSignature += ", ";
}
}
methodSignature += ")";
MethodsAsStrings.Add(methodSignature);
}
return MethodsAsStrings.ToArray();
}
/// <summary>
/// You can call only "public static" functions
/// </summary>
/// <param name="type">The class to be called its function</param>
/// <param name="Input">the calling input. Example: MyFunction(5,\"MyString\",4.8)</param>
public static bool Call(Type type, string Input)
{
ParseInput(Input, out string methodName, out List<string> parameters);
MethodInfo[] methods = FindMethods(methodName, type.GetMethods());
DebugPrint(methods.Length + " functions found");
foreach (var method in methods)
{
object[]? convertedParameters = ConvertParameters(parameters, method);
if (convertedParameters == null)
{
DebugPrint("parameters don't match. Trying another method");
continue;
}
try
{
DebugPrint("method has been calling");
for (int i = 0; i < convertedParameters.Length; i++)
{
if(convertedParameters[i] != null)
DebugPrint("parameter " + i + " is " + convertedParameters[i].GetType().Name + " and its value is " + convertedParameters[i].ToString());
}
method.Invoke(null, convertedParameters);
return true;
}
catch (Exception)
{
return false;
throw;
}
}
return false;
}
static void ParseInput(string input, out string methodName, out List<string> parameters)
{
try
{
methodName = input.Substring(0, input.IndexOf("("));
string paramString = input.Substring(input.IndexOf("(") + 1, input.LastIndexOf(")") - input.IndexOf("(") - 1);
parameters = new List<string>(paramString.Split(','));
if (parameters.Count == 1 && parameters[0] == "")
parameters.Clear();//input is null but paramString.Split doesn't convert null. so you should make the list empty.
}
catch (Exception e)
{
DebugPrint("An exception occured while parsing. Exception message: " + e.Message);
throw;
}
}
static MethodInfo[] FindMethods(string methodName, MethodInfo[] methods)
{
return methods.Where(m => m.Name == methodName).ToArray();
}
static object?[]? ConvertParameters(List<string> parameters, MethodInfo method)
{
DebugPrint("Entered parameters:");
foreach (var parameter in parameters)
{
DebugPrint(parameter);
}
ParameterInfo[] methodParams = method.GetParameters();
//if methodParams.Length less than parameters.Count then the function should be called is not this function. It should be another overload.
if (methodParams.Length < parameters.Count)
return null;
List<object?> convertedParams = new();
for (int i = 0; i < methodParams.Length; i++)
{
Type paramType = methodParams[i].ParameterType;
try
{
if (i >= parameters.Count)
if (methodParams[i].HasDefaultValue)
convertedParams.Add(methodParams[i].DefaultValue);
else
return convertedParams.ToArray();
else
{
//if it's string, then return the string without quotes
if (parameters[i].StartsWith("\"") && parameters[i].EndsWith("\"") && paramType == typeof(string))
convertedParams.Add(parameters[i].Substring(1, parameters[i].Length - 2));
else
convertedParams.Add(Convert.ChangeType(parameters[i], paramType));
}
}
catch (Exception ex)
{
DebugPrint("Parameter converting error: " + ex.Message);
DebugPrint("type: " + paramType.Name);
return null;
}
}
return convertedParams.ToArray();
}
}
@muhammetozeski
Copy link
Author

muhammetozeski commented Apr 27, 2024

You can simply create a console command program with this. Call your functions from console without needing any customisation for every function.

static void Main(string[] args)
{
    Type TheClass = typeof(TestClass);

    CallFunctions.PrintDebugMessages = false;
    Console.WriteLine("You can call these functions:");
    string[] functions = CallFunctions.GetMethodsAsStrings(TheClass);
    for (int i = 0; i < functions.Length; i++)
    {
        Console.WriteLine(functions[i]);
    }
    while (true)
    {
        try
        {
            Console.WriteLine("Write command:");
            string? input = null;
            while (string.IsNullOrWhiteSpace(input))
            {
                input = Console.ReadLine();
            }
            bool result = CallFunctions.Call(typeof(TestClass), input);
            if (result)
            {
                Console.WriteLine("method is called succesfully");
            }
            else
            {
                Console.WriteLine("method couldn't be called");
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("Exception occured: " + e.Message);
        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment