I asked a question a while ago about a method that orders a List<Task<T>>>
by their completion that also returns an int
representing the index of the completed Task
in the original List<Task<T>>
given.
I have the inkling that I might not need to return this int
to determine which specific Task
has completed and that I can interrogate the returned Task
for this information.
As a side note, I have since altered the method to order a List<Task>
. I originally used Task<T>
, which returned a bool
to represent if the Task<T>
was successful in its job or not. I now simply throw a subclass of Exception
which provides more information about how and why a Task
failed.
My idea for this question came from the issue that when a Task<int>
from this method throws an Exception
I have no way to determine which specific Task
threw the Exception
because I cannot interrogate the Task<int>.Result
for the Task
's original index.
So again, if I can interrogate the Task<int>
(now simply just Task
) that is returned for the Task
it refers to from the original list, I can simply compare the references.
Here is the method as it now exists (Credit to Servy for the original code for this method. Also possibly this blog post from Jon Skeet)
public static IEnumerable<Task<int>> OrderByCompletion(IEnumerable<Task> tasks)
{
var taskList = tasks.ToList();
var taskSources = new BlockingCollection<TaskCompletionSource<int>>();
var taskSourceList = new List<TaskCompletionSource<int>>(taskList.Count);
for (int i = 0; i < taskList.Count; i++)
{
var task = taskList[i];
var newSource = new TaskCompletionSource<int>();
taskSources.Add(newSource);
taskSourceList.Add(newSource);
int index = i;
task.ContinueWith(t =>
{
var source = taskSources.Take();
if (t.IsCanceled)
source.TrySetCanceled();
else if (t.IsFaulted)
source.TrySetException(t.Exception.InnerExceptions);
else if (t.IsCompleted)
source.TrySetResult(index);
}, CancellationToken.None, TaskContinuationOptions.PreferFairness, TaskScheduler.Default);
}
return taskSourceList.Select(tcs => tcs.Task);
}
My first issue is that this method uses TaskCompletionSource<T>
to track the TResult
of the Task
from the parameter list. Seeing as the List<Task>>
now returns no value and uses Exception
s this is useless, though its usage is unavoidable because there is no non generically parameterized TaskCompletionSource<T>
But this is not really a problem because I can just return some trash value.
So, to the question itself, can I interrogate the Task<(unused return value)>
to get a reference to the Task
it tracks?
From what I can tell the TaskCompletionSource<T>
has no information about the Task
it is tracking. It simply "looks like" the original Task
.
Is the only option to subclass TaskCompletionSource<T>
to add one property that refers to the Task
it tracked, set the value of the property in the method, and then interrogate that property for the reference?
public class TaskHoldingTaskCompletionSource<T> : TaskCompletionSource<T>
{
public Task OriginalTask { get; set; }
}