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 Exceptions 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; }
}