4

I am looking to get the method/action name from a task in C#. Specifically I am implementing a custom task scheduler, and would like to generate statistics on the duration a task runs, which I will then aggregate by the method running inside of the task. In the visual studio debugger you can access this and see the m_action private variable, as well as the debugger display annotation, displays it as Method={0}. Is there any way to get access to this from the Task itself?

svick
  • 236,525
  • 50
  • 385
  • 514
Superman
  • 3,686
  • 6
  • 34
  • 46
  • 4
    The question is, how useful that would be. The lambda expression you (typically) pass to a `Task` are turned into methods by the C# compiler and get compiler-generated names. Those names are not [very telling](http://stackoverflow.com/q/10450311/21567) and it'll be hard to re-associate them with the actual code. – Christian.K Aug 23 '12 at 06:06
  • @Christian.K If were okay with limiting yourself only to very simple lambdas, you could use `Expression` instead of a delegate and get the method name from that. (Of course, an `Expression` can invoke several methods, so you would have to somehow choose which one to show. And it also may not call any methods.) – svick Aug 23 '12 at 13:46

3 Answers3

4

You could inherit from Task to make this real easy... I'm just going to implement the first constructor here for the example:

public class NamedTask : Task {
    public string MethodName { get; set; }
    public NamedTask(Action action) : base(action) {
        MethodName = action.Method.Name;
    }
    public NamedTask(Action action, CancellationToken cancellationToken) : base(action, cancellationToken) {}
    public NamedTask(Action action, TaskCreationOptions creationOptions) : base(action, creationOptions) {}
    public NamedTask(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions) : base(action, cancellationToken, creationOptions) {}
    public NamedTask(Action<object> action, object state) : base(action, state) {}
    public NamedTask(Action<object> action, object state, CancellationToken cancellationToken) : base(action, state, cancellationToken) {}
    public NamedTask(Action<object> action, object state, TaskCreationOptions creationOptions) : base(action, state, creationOptions) {}
    public NamedTask(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions) : base(action, state, cancellationToken, creationOptions) {}
}

After that...

NamedTask task = new NamedTask(() => AsyncMethod(arg1, arg2, argN));
string methodName = task.MethodName; // there's the name!

More examples. Inherit from Task<T>:

public class NamedTask<T> : Task<T> {
    public string MethodName { get; set; }
    public NamedTask(Func<T> function) : base(function) {
        MethodName = function.Method.Name;
    }
    public NamedTask(Func<T> function, string methodName) : base(function) {
        MethodName = methodName;
    }
    ...
}

Handle anonymous methods:

NamedTask<bool> task2 = new NamedTask<bool>(() => {
                // some arbitrary code
                return true;
    });

NamedTask<bool> task3 = new NamedTask<bool>(() => {
                // some arbitrary code
                return true;
    }, "ReturnTrueMethod");

string methodName2 = task2.MethodName; // returns "<LongRunning_Async>b__19"
string methodName3 = task3.MethodName; // returns "ReturnTrueMethod"
James
  • 2,823
  • 22
  • 17
  • Interesting idea. Except: 1. `MethodInfo.ToString()` doesn't return just the method, name, it returns the whole signature. E.g. something like `Void AsyncMethod()`. To get just the name, use the `Name` property. 2. As noted by Christian in a comment, this won't give useful results for lambdas. – svick Aug 23 '12 at 13:42
  • Good points. For #1, if you really want just the method name, you can parse it out of the signature. For #2, in my example, I used a lambda... so I think maybe you mean it won't give anything useful for anonymous methods? In this case, it will still return a method name - it will just be the compiler generated method name for the anonymous method. Here you could make another constructor that allowed you to specify your own name for the anonymous method when creating the task. – James Aug 23 '12 at 17:57
  • Like I said, you can use the `Name` property, no need to parse anything. – svick Aug 23 '12 at 18:01
  • Ah, action.Method.Name. Yes! Nice one. I will change the post to reflect this. – James Aug 23 '12 at 18:07
3

Well, you could use reflection to get at the private m_action field, given a Task variable task:

    var fieldInfo = typeof(Task).GetField("m_action", BindingFlags.Instance | BindingFlags.NonPublic);
    Delegate action = fieldInfo.GetValue(task) as Delegate;

Then get the Name of the method and the DeclaringType:

    var name = action.Method.Name;
    var type = action.Method.DeclaringType.FullName;

To get the fully qualified method (type + "." + name)...

But, as soon as the task executes to completion, m_action is null. I'm not sure how this would apply with TaskFactory.StartNew...

Peter Ritchie
  • 35,463
  • 9
  • 80
  • 98
0
  1. Try reflection to get m_action variable.
  2. Try getting info from Envorinment.StackTrace from inside the task or directly called methods by it.
AgentFire
  • 8,944
  • 8
  • 43
  • 90