Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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)?

like image 367
AngryHacker Avatar asked Oct 19 '11 23:10

AngryHacker


2 Answers

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);
    }
}
like image 139
marcind Avatar answered Oct 05 '22 05:10

marcind


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.

like image 37
used2could Avatar answered Oct 05 '22 07:10

used2could