MethodBase Object of async function

1.2k views Asked by At

in my application i have a little action log. In there I save the last few calls of certain methods in my application like this:

saveLastAction(MethodBase.GetCurrentMethod(), getCurrentStatus(), item, /*loadFresh*/ false);

It allows me to navigate and refresh through my last actions

    public void Refresh(bool loadFresh = true)
    {
        if (!isInitialized) return;

        try
        {
            lastStatus = getCurrentStatus();

            var parameters = lastActionsParameters.Pop();
            var method = lastActions.Pop();

            //Set the load Fresh parameter to True
            parameters[method.GetParameters().First(pi => pi.Name == "loadFresh").Position] = loadFresh;

            //Invoke the Method again with the adopted parameters
            method.Invoke(this, parameters);

        }
        catch
        {
        }
    }

I worked perfectly until i changed one of the methods which calls saveLastAction to async. Since then MethodBase.GetCurrentMethod() only returns the MoveNext function and not the actual function i called.

Is there a way to get to the actual MethodBase Object of the called function, whether at the time of saving or calling doesn't matter.

Best regards lolsharp

3

There are 3 answers

0
Gusdor On

This is going to a get a whole lot easier in C#7 using local functions:

public Task DoAThingAsync()
{
    var method = MethodBase.GetCurrentMethod();
    LogMethod(method);

    return doWork();

    // Define inner function to actually do the work
    async Task doWork()
    {
         // Do the work here
    }
}

You can already achieve this with anonymous methods or delegates but this makes the code it very clearn.

0
Peter Duniho On

Do you really need the MethodBase object? Or is it sufficient to just log enough detail that a human can figure out which method it was?

If the latter, then it's fairly simple: note that the method that is executing, is actually contained in a compiler-generated class where the class name itself contains the name of the method the compiler rewrote to generate that class.

So in your saveLastAction() method, where presumably you log the MethodBase.Name property value, you should first check the MethodBase.DeclaringType.Name value, and if it looks like <SomeMethodName>d_5 (where the specifics after the closing angle bracket may vary as well), you can use the text within the angle brackets for the method name instead of the MethodBase.Name property value.

Note that only the compiler is allowed to generate class names with those angle brackets, so you don't have to worry about getting a false positive.

As an added bonus, this will allow your logging code to work with iterator methods too. :)

If you need the MethodBase object itself, that's a bit more complicated, but would follow the same basic strategy. Get the method name, and then look that method up in the MethodBase.DeclaringType.DeclaringType type (i.e. the type that contains the compiler-generated type). Note that if there is more than one overload for the method, identifying the specific overload that was used may be tricky or impractical.

1
lolsharp On

I created a little help function:

private MethodInfo getMethodBase(object caller, [CallerMemberName]string methodName = "")
    {
        //Binding Flags to include private functions
        return caller.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
    }

Using the CallerMemberNameAttribute directly didn't work because I was also using params parameter. In the end a very similar to Peters approach.