7

Having instantiated one or more Task objects in C#, like this for example:

var tasks = new List<Task>
{
    Task.Factory.StartNew(MyWorker.DoWork),
    Task.Factory.StartNew(AnotherWorker.DoOtherWork)
};

Is there a way to get the Action method from the task object? In other words, can I return the Action of MyWorker.DoWork from its task?

I'm trying to be able to log the status of each task, like this:

Task.WaitAll(tasks.ToArray(), new TimeSpan(0, 0, 1));

var msg = tasks.Aggregate(string.Empty,
    (current, task) =>
        current + $"{task.Action}: {task.Status}{Environment.NewLine}");

The string value of msg would be:

MyWorker.DoWork RanToCompletion
AnotherWorker.DoOtherWork Running

(The {task.Action} portion of my sample doesn't exist and wouldn't compile, of course)

Chris Schiffhauer
  • 17,102
  • 15
  • 79
  • 88
  • 1
    What should it print if you do this: `Task.Factory.StartNew(() => {})` ? – Rob Aug 20 '15 at 21:05
  • Also - see this answer: http://stackoverflow.com/a/12098662/563532 - You might have to modify it a bit to include the declaring class (MyWorker / AnotherWork) etc. Do be aware, though, of situations where methods do not necessarily have human-friendly names as per my above comment – Rob Aug 20 '15 at 21:07
  • 1
    Create a wrapper service that you use to create tasks. Inside that, create a task and along with it store the Action to reference later on. Essentially using the adaptor pattern. – Patrick Magee Aug 20 '15 at 21:12
  • 1
    possible duplicate of [Getting the method name of a task](http://stackoverflow.com/questions/12085347/getting-the-method-name-of-a-task) – CoderDennis Aug 20 '15 at 21:26

1 Answers1

8

The delegate is stored in the m_action data member on the Task class. You can see that in the reference source.

However, that data member is internal and there's no public way to get to it. You can however use reflection to pick into the insides of a task and look at the contents of m_action.

For example this:

var fooTask = new Task(Foo);
var fieldInfo = typeof(Task).GetField("m_action", BindingFlags.NonPublic | BindingFlags.Instance);
var value = fieldInfo.GetValue(fooTask);
Console.WriteLine(((Action)value).Method);

Outputs (in my specific example):

Void Foo()

A better design option would be to just start all your tasks from a single place in your code that registers all the information you would need outside of the Task itself and use that to log the status.

i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • 1
    Thank you. Because `m_action` is set to null when the `Task` completes, and I call `Task.WaitAll` before interrogating the tasks, I followed your suggestion to change the design. Very informative answer in both the technical aspect and the design suggestion. – Chris Schiffhauer Aug 21 '15 at 22:11