Beispiel #1
0
        /// <summary>Creates a proxy <see cref="Task{TResult}"/> that represents the asynchronous operation of a <see cref="Task{Task{TResult}}"/>.</summary>
        /// <param name="task">The <see cref="Task{Task{TResult}}"/> to unwrap.</param>
        /// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous operation of the provided <see cref="Task{Task{TResult}}"/>.</returns>
        public static Task <TResult> Unwrap <TResult>(this Task <Task <TResult> > task)
        {
            if (task == null)
            {
                throw new ArgumentNullException(nameof(task));
            }

            // If the task hasn't completed or was faulted/canceled, wrap it in an unwrap promise. Otherwise,
            // it completed successfully.  Return its inner task to avoid unnecessary wrapping, or if the inner
            // task is null, return a canceled task to match the same semantics as CreateUnwrapPromise.
            return
                (!task.IsCompletedSuccessfully ? Task.CreateUnwrapPromise <TResult>(task, lookForOce: false) :
                 task.Result ??
                 Task.FromCanceled <TResult>(new CancellationToken(true)));
        }
Beispiel #2
0
        /// <summary>
        /// Creates a proxy <see cref="System.Threading.Tasks.Task">Task</see> that represents the
        /// asynchronous operation of a Task{Task}.
        /// </summary>
        /// <remarks>
        /// It is often useful to be able to return a Task from a <see cref="System.Threading.Tasks.Task{TResult}">
        /// Task{TResult}</see>, where the inner Task represents work done as part of the outer Task{TResult}.  However,
        /// doing so results in a Task{Task}, which, if not dealt with carefully, could produce unexpected behavior.  Unwrap
        /// solves this problem by creating a proxy Task that represents the entire asynchronous operation of such a Task{Task}.
        /// </remarks>
        /// <param name="task">The Task{Task} to unwrap.</param>
        /// <exception cref="T:System.ArgumentNullException">The exception that is thrown if the
        /// <paramref name="task"/> argument is null.</exception>
        /// <returns>A Task that represents the asynchronous operation of the provided Task{Task}.</returns>
        public static Task Unwrap(this Task <Task> task)
        {
            if (task == null)
            {
                throw new ArgumentNullException("task");
            }
#if SILVERLIGHT && !FEATURE_NETCORE // CoreCLR only
            bool result;

            // tcs.Task serves as a proxy for task.Result.
            // AttachedToParent is the only legal option for TCS-style task.
            var tcs = new TaskCompletionSource <Task>(task.CreationOptions & TaskCreationOptions.AttachedToParent);

            // Set up some actions to take when task has completed.
            task.ContinueWith(delegate
            {
                switch (task.Status)
                {
                // If task did not run to completion, then record the cancellation/fault information
                // to tcs.Task.
                case TaskStatus.Canceled:
                case TaskStatus.Faulted:
                    result = tcs.TrySetFromTask(task);
                    Contract.Assert(result, "Unwrap(Task<Task>): Expected TrySetFromTask #1 to succeed");
                    break;

                case TaskStatus.RanToCompletion:
                    // task.Result == null ==> proxy should be canceled.
                    if (task.Result == null)
                    {
                        tcs.TrySetCanceled();
                    }

                    // When task.Result completes, take some action to set the completion state of tcs.Task.
                    else
                    {
                        task.Result.ContinueWith(_ =>
                        {
                            // Copy completion/cancellation/exception info from task.Result to tcs.Task.
                            result = tcs.TrySetFromTask(task.Result);
                            Contract.Assert(result, "Unwrap(Task<Task>): Expected TrySetFromTask #2 to succeed");
                        }, TaskContinuationOptions.ExecuteSynchronously).ContinueWith(antecedent =>
                        {
                            // Clean up if ContinueWith() operation fails due to TSE
                            tcs.TrySetException(antecedent.Exception);
                        }, TaskContinuationOptions.OnlyOnFaulted);
                    }
                    break;
                }
            }, TaskContinuationOptions.ExecuteSynchronously).ContinueWith(antecedent =>
            {
                // Clean up if ContinueWith() operation fails due to TSE
                tcs.TrySetException(antecedent.Exception);
            }, TaskContinuationOptions.OnlyOnFaulted);

            // Return this immediately as a proxy.  When task.Result completes, or task is faulted/canceled,
            // the completion information will be transfered to tcs.Task.
            return(tcs.Task);
#else // Desktop or CoreSys
            // Creates a proxy Task and hooks up the logic to have it represent the task.Result
            Task promise = Task.CreateUnwrapPromise <VoidResult>(task, lookForOce: false);

            // Return the proxy immediately
            return(promise);
#endif
        }