You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何判断.NET中的对象是否为可等待(Awaitable)对象?

判断对象是否可等待的最佳实践

我有一个返回任意类型对象的方法,需要判断该对象是否为可等待(Awaitable)类型,若是则等待并获取最终结果。仅检查Task/ValueTask及其泛型版本并不足够,因为还存在其他符合可等待约定的类型。我想实现类似如下逻辑,但C#中并没有现成的Awaitable类型:

object result = GetResult();

object actualResult = result is Awaitable awaitable // "Awaitable" 并不存在
    ? await awaitable
    : result;

根据C#语言规范,一个表达式t是可等待的,需满足以下条件之一:

  • t的编译时类型为dynamic
  • t拥有可访问的实例或扩展方法GetAwaiter(无参数、无类型参数),且该方法的返回类型A需同时满足:
    • 实现System.Runtime.CompilerServices.INotifyCompletion接口
    • 包含可访问的、可读的bool类型实例属性IsCompleted
    • 包含可访问的无参数、无类型参数实例方法GetResult

以下是判断对象可等待性的几种最佳方式:

1. 反射+动态调用实现检查与等待

由于可等待性是基于结构约定而非接口实现,我们可以通过反射验证这些约定,再结合dynamic自动处理await逻辑:

辅助检查方法

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Linq;
using System.Collections.Concurrent;

public static class AwaitableChecker
{
    // 缓存类型的可等待性检查结果,提升性能
    private static readonly ConcurrentDictionary<Type, bool> _awaitableTypeCache = new();

    public static bool IsAwaitable(object obj)
    {
        if (obj == null) return false;
        Type type = obj.GetType();

        if (_awaitableTypeCache.TryGetValue(type, out bool isAwaitable))
        {
            return isAwaitable;
        }

        bool result = CheckAwaitableType(type);
        _awaitableTypeCache.TryAdd(type, result);
        return result;
    }

    private static bool CheckAwaitableType(Type type)
    {
        // 查找实例GetAwaiter方法
        MethodInfo getAwaiterMethod = type.GetMethod("GetAwaiter", Type.EmptyTypes);
        
        // 实例方法不存在时,查找扩展方法
        if (getAwaiterMethod == null)
        {
            getAwaiterMethod = FindExtensionGetAwaiterMethod(type);
        }

        if (getAwaiterMethod == null) return false;
        Type awaiterType = getAwaiterMethod.ReturnType;

        // 验证awaiter的三个核心约定
        bool implementsINotifyCompletion = typeof(INotifyCompletion).IsAssignableFrom(awaiterType);
        PropertyInfo isCompletedProp = awaiterType.GetProperty("IsCompleted", typeof(bool));
        MethodInfo getResultMethod = awaiterType.GetMethod("GetResult", Type.EmptyTypes);

        return implementsINotifyCompletion 
            && isCompletedProp != null && isCompletedProp.CanRead 
            && getResultMethod != null;
    }

    private static MethodInfo FindExtensionGetAwaiterMethod(Type targetType)
    {
        // 扫描所有已加载程序集中的扩展方法
        return AppDomain.CurrentDomain.GetAssemblies()
            .SelectMany(asm => asm.GetTypes())
            .Where(t => t.IsSealed && !t.IsGenericType && !t.IsNested)
            .SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public))
            .Where(m => m.IsDefined(typeof(ExtensionAttribute), false))
            .Where(m => m.Name == "GetAwaiter" 
                        && m.GetParameters().Length == 1 
                        && m.GetParameters()[0].ParameterType.IsAssignableFrom(targetType))
            .FirstOrDefault();
    }
}

使用示例

object result = GetResult();
object actualResult = result;

if (AwaitableChecker.IsAwaitable(result))
{
    dynamic awaitable = result;
    actualResult = await awaitable;
}

这里借助dynamic让编译器自动处理await的完整流程,无需手动调用GetAwaiter、检查IsCompleted等步骤,既简洁又符合规范。

2. 参考ASP.NET Core的内部实现

你提到的ASP.NET MVC控制器处理可等待返回值的逻辑,核心也是基于反射检查可等待约定+动态调用。其内部通过TaskHelper等工具类封装了类似逻辑,本质和上面的实现思路一致——先验证类型是否符合可等待约定,再通过动态类型执行await操作。

关键注意事项

  • 扩展方法扫描范围:上述示例扫描了所有已加载程序集的扩展方法,若场景明确,可缩小扫描范围提升性能。
  • 性能优化:通过ConcurrentDictionary缓存类型的可等待性检查结果,避免重复反射操作。
  • dynamic的开销:dynamic会带来少量性能损耗,但在绝大多数业务场景下可忽略不计。

内容的提问来源于stack exchange,提问作者Dan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 04:00:14