// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Reflection; using System.Runtime.CompilerServices; using System.Threading.Tasks; namespace CommunityToolkit.Common; /// /// Helpers for working with tasks. /// public static class TaskExtensions { /// /// Gets the result of a if available, or otherwise. /// /// The input instance to get the result for. /// The result of if completed successfully, or otherwise. /// /// This method does not block if has not completed yet. Furthermore, it is not generic /// and uses reflection to access the property and boxes the result if it's /// a value type, which adds overhead. It should only be used when using generics is not possible. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static object? GetResultOrDefault(this Task task) { // Check if the instance is a completed Task if ( #if NETSTANDARD2_1 task.IsCompletedSuccessfully #else task.Status == TaskStatus.RanToCompletion #endif ) { // We need an explicit check to ensure the input task is not the cached // Task.CompletedTask instance, because that can internally be stored as // a Task for some given T (eg. on .NET 6 it's VoidTaskResult), which // would cause the following code to return that result instead of null. if (task != Task.CompletedTask) { // Try to get the Task.Result property. This method would've // been called anyway after the type checks, but using that to // validate the input type saves some additional reflection calls. // Furthermore, doing this also makes the method flexible enough to // cases whether the input Task is actually an instance of some // runtime-specific type that inherits from Task. PropertyInfo? propertyInfo = task.GetType().GetProperty(nameof(Task.Result)); // Return the result, if possible return propertyInfo?.GetValue(task); } } return null; } /// /// Gets the result of a if available, or otherwise. /// /// The type of to get the result for. /// The input instance to get the result for. /// The result of if completed successfully, or otherwise. /// This method does not block if has not completed yet. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T? GetResultOrDefault(this Task task) { #if NETSTANDARD2_1 return task.IsCompletedSuccessfully ? task.Result : default; #else return task.Status == TaskStatus.RanToCompletion ? task.Result : default; #endif } }