How to execute a private static method with optional parameters via reflection?

18,948

Solution 1

Optional parameter values in C# are compiled by injection those values at the callsite. I.e. even though your code is

Foo.Bar()

The compiler actually generates a call like

Foo.Bar("")

When finding the method you need to treat the optional parameters as regular parameters.

var method = typeof(Foo).GetMethod("Bar", BindingFlags.Static | BindingFlags.NonPublic);

If you know exactly what values you want to invoke the method with you can do:

method.Invoke(obj: null, parameters: new object[] { "Test" });

If you only have some of the parameters and want to honor the values of the default ones you have to inspect the method's ParameterInfo objects to see if the parameters are optional and what those values are. For example to print out the default values of those parameters you could use the following code:

foreach (ParameterInfo pi in method.GetParameters())
{
    if (pi.IsOptional)
    {
        Console.WriteLine(pi.Name + ": " + pi.DefaultValue);
    }
}

Solution 2

Using this class

  public class Foo
  {
    private static void Bar(string key = "undefined key", string value = "undefined value")
    {
      Console.WriteLine(string.Format("The key is '{0}'", key));
      Console.WriteLine(string.Format("The value is '{0}'", value));
    }
  }

You can use the following code to call it with the default values

  MethodInfo mi = typeof(Foo).GetMethod("Bar", BindingFlags.NonPublic | BindingFlags.Static);
  ParameterInfo[] pis = mi.GetParameters();

  object[] parameters = new object[pis.Length];

  for (int i = 0; i < pis.Length; i++)
  {
    if (pis[i].IsOptional)
    {
      parameters[i] = pis[i].DefaultValue;
    }
  }

  mi.Invoke(null, parameters);

If the method had some non-optional parameters, you will have to insert them into the parameters array before invoking the method.

E.g

private static void Bar(int number, string key = "undefined key", string value = "undefined")

Would require you to do

parameters[0] = "23"

Before invoking

Solution 3

Something i wrote for my unit tests:

    /// <summary>
    /// Attempts to execute a function and provide the result value against the provided source object even if it is private and/or static. Just make sure to provide the correct BindingFlags to correctly identify the function.
    /// </summary>
    /// <typeparam name="TReturn">The expected return type of the private method.</typeparam>
    /// <param name="type">The object's Type that contains the private method.</param>
    /// <param name="source">The object that contains the function to invoke. If looking for a static function, you can provide "null".</param>
    /// <param name="methodName">The name of the private method to run.</param>
    /// <param name="flags">Binding flags used to search for the function. Example: (BindingFlags.NonPublic | BindingFlags.Static) finds a private static method.</param>
    /// <param name="output">The invoked function's return value.</param>
    /// <param name="methodArgs">The arguments to pass into the private method.</param>
    /// <returns>Returns true if function was found and invoked. False if function was not found.</returns>
    private static bool TryInvokeMethod<TReturn>(Type type, object source, string methodName, BindingFlags flags, out TReturn output, params object[] methodArgs)
    {
        var method = type.GetMethod(methodName, flags);
        if(method != null)
        {
            output = (TReturn)method.Invoke(source, methodArgs);
            return true;
        }

        // Perform some recursion to walk the inheritance. 
        if(type.BaseType != null)
        {
            return TryInvokeMethod(type.BaseType, source, methodName, flags, out output, methodArgs);
        }

        output = default(TReturn);
        return false;
    }

Then call it like so to invoke a private static function:

var success = TryInvokeMethod(typeof(Foo), null, "MyPrivateStaticFunc", BindingFlags.NonPublic | BindingFlags.Static, out result, arg1ToPass);

Disclaimer: I use this for functions with a return value only. Attempting to execute a method with no return value will throw an exception.

Share:
18,948
AngryHacker
Author by

AngryHacker

Updated on June 15, 2022

Comments

  • AngryHacker
    AngryHacker almost 2 years

    I have a class with a private static method with an optional parameter. How do I invoke it from another class via Reflection? There is a similar question, but it does not address static method or optional parameters.

    public class Foo {
        private static void Bar(string key = "") {
           // do stuff
        }
    }
    

    How do I invoke Foo.Bar("test") and Foo.Bar() (e.g. without passing the optional parameter)?