C# 如何识别方法实现是否标记为async/可以仅基于其使用Roslyn的接口异步调用?

C# 如何识别方法实现是否标记为async/可以仅基于其使用Roslyn的接口异步调用?,c#,roslyn,roslyn-code-analysis,C#,Roslyn,Roslyn Code Analysis,背景信息 我正在为VisualStudio构建一个基于Roslyn的代码修复程序,用于处理类未实现接口(或缺少该接口的一部分)的情况 接口通常是第三方代码,例如Microsoft的IDocumentClient 然后,我创建了该接口的一个实现,其中对方法和属性的调用被“包装”,方法和属性的实际执行由3个助手方法中最相关的候选者来处理,作为修饰实现的一部分。这些助手方法处理不同返回类型的场景,包括void返回、非任务类型和泛型任务类型 helper方法调用Polly库;对于返回通用任务类型的帮助程

背景信息

我正在为VisualStudio构建一个基于Roslyn的代码修复程序,用于处理类未实现接口(或缺少该接口的一部分)的情况

接口通常是第三方代码,例如Microsoft的IDocumentClient

然后,我创建了该接口的一个实现,其中对方法和属性的调用被“包装”,方法和属性的实际执行由3个助手方法中最相关的候选者来处理,作为修饰实现的一部分。这些助手方法处理不同返回类型的场景,包括void返回、非任务类型和泛型任务类型

helper方法调用
Polly
库;对于返回通用任务类型的帮助程序,特别是
Polly
ExecuteAsync
方法,它执行传递的方法委托,并根据用户指定的行为(重试、断路器等)处理异常

我的项目的代码可以在Github上找到

问题

我需要能够通过接口声明中包含的信息来确定我正在创建的方法是否是异步的

这将决定两件事:

  • 如果我的实现应该用
    async
    修饰符标记

  • 如果实现可以异步调用,那么允许我决定方法的实现(已包装)是否可以,然后是否应该由包装代码异步处理

  • 我不能依赖任何其他外部信息

    我所考虑的

    我已经研究了使用方法的返回类型,以确定它是否是
    任务
    ,但在某些情况下,它的接口中的方法声明可能是“returning void”,即使它的实际实现是用异步修饰符标记的,或者可以异步方式调用

    检查异步后缀的名称显然不可靠;并不是每个人都遵循这样的惯例

    问题

    是否有可靠的方法来识别方法实现是否是异步的,即是否应该用
    async
    来修饰它,并且是否可以使用Roslyn仅基于其接口声明异步处理它

    (请参阅评论讨论,它表明了这个问题的演变)

    Roslyn有一个内部版本,完全符合您的要求

    您可以复制它:

        /// <summary>
        /// If the <paramref name="symbol"/> is a method symbol, returns <see langword="true"/> if the method's return type is "awaitable", but not if it's <see langword="dynamic"/>.
        /// If the <paramref name="symbol"/> is a type symbol, returns <see langword="true"/> if that type is "awaitable".
        /// An "awaitable" is any type that exposes a GetAwaiter method which returns a valid "awaiter". This GetAwaiter method may be an instance method or an extension method.
        /// </summary>
        public static bool IsAwaitableNonDynamic(this ISymbol symbol, SemanticModel semanticModel, int position)
        {
            IMethodSymbol methodSymbol = symbol as IMethodSymbol;
            ITypeSymbol typeSymbol = null;
    
            if (methodSymbol == null)
            {
                typeSymbol = symbol as ITypeSymbol;
                if (typeSymbol == null)
                {
                    return false;
                }
            }
            else
            {
                if (methodSymbol.ReturnType == null)
                {
                    return false;
                }
            }
    
            // otherwise: needs valid GetAwaiter
            var potentialGetAwaiters = semanticModel.LookupSymbols(position,
                                                                   container: typeSymbol ?? methodSymbol.ReturnType.OriginalDefinition,
                                                                   name: WellKnownMemberNames.GetAwaiter,
                                                                   includeReducedExtensionMethods: true);
            var getAwaiters = potentialGetAwaiters.OfType<IMethodSymbol>().Where(x => !x.Parameters.Any());
            return getAwaiters.Any(VerifyGetAwaiter);
        }
    
        private static bool VerifyGetAwaiter(IMethodSymbol getAwaiter)
        {
            var returnType = getAwaiter.ReturnType;
            if (returnType == null)
            {
                return false;
            }
    
            // bool IsCompleted { get }
            if (!returnType.GetMembers().OfType<IPropertySymbol>().Any(p => p.Name == WellKnownMemberNames.IsCompleted && p.Type.SpecialType == SpecialType.System_Boolean && p.GetMethod != null))
            {
                return false;
            }
    
            var methods = returnType.GetMembers().OfType<IMethodSymbol>();
    
            // NOTE: (vladres) The current version of C# Spec, §7.7.7.3 'Runtime evaluation of await expressions', requires that
            // NOTE: the interface method INotifyCompletion.OnCompleted or ICriticalNotifyCompletion.UnsafeOnCompleted is invoked
            // NOTE: (rather than any OnCompleted method conforming to a certain pattern).
            // NOTE: Should this code be updated to match the spec?
    
            // void OnCompleted(Action) 
            // Actions are delegates, so we'll just check for delegates.
            if (!methods.Any(x => x.Name == WellKnownMemberNames.OnCompleted && x.ReturnsVoid && x.Parameters.Length == 1 && x.Parameters.First().Type.TypeKind == TypeKind.Delegate))
            {
                return false;
            }
    
            // void GetResult() || T GetResult()
            return methods.Any(m => m.Name == WellKnownMemberNames.GetResult && !m.Parameters.Any());
        }
    
    //
    ///如果是方法符号,则在方法的返回类型为“可等待”时返回,但在方法的返回类型为“可等待”时不返回。
    ///如果是类型符号,则返回该类型是否为“可等待”。
    ///“awaitable”是公开GetAwaiter方法并返回有效“awaiter”的任何类型。此GetAwaiter方法可以是实例方法或扩展方法。
    /// 
    公共静态bool为可等待非动态(此为符号,SemanticModel SemanticModel,int位置)
    {
    IMethodSymbol methodSymbol=符号作为IMethodSymbol;
    ITypeSymbol typeSymbol=null;
    if(methodSymbol==null)
    {
    typeSymbol=作为ITypeSymbol的符号;
    if(typeSymbol==null)
    {
    返回false;
    }
    }
    其他的
    {
    if(methodSymbol.ReturnType==null)
    {
    返回false;
    }
    }
    //否则:需要有效的GetAwaiter
    var potentialGetAwaiters=语义模型LookupSymbols(位置,
    容器:typeSymbol??methodSymbol.ReturnType.OriginalDefinition,
    名称:WellKnownMemberNames.GetWaiter,
    includeReducedExtensionMethods:true);
    var getAwaiters=potentialGetAwaiters.OfType()。其中(x=>!x.Parameters.Any());
    返回GetAwaiter.Any(验证GetAwaiter);
    }
    专用静态布尔验证getAwaiter(IMethodSymbol getAwaiter)
    {
    var returnType=getAwaiter.returnType;
    if(returnType==null)
    {
    返回false;
    }
    //布尔已完成{get}
    如果(!returnType.GetMembers().OfType().Any(p=>p.Name==WellKnownMemberNames.IsCompleted&&p.Type.SpecialType==SpecialType.System\u Boolean&&p.GetMethod!=null))
    {
    返回false;
    }
    var methods=returnType.GetMembers().OfType();
    //注:(vladres)当前版本的C#Spec,§7.7.7.3“等待表达式的运行时评估”要求
    //注意:调用接口方法INotifyCompletion.OnCompleted或ICriticalNotifyCompletion.UnsafeOnCompleted
    //注:(而不是符合特定模式的任何未完成方法)。
    //注意:是否应更新此代码以匹配规范?
    //未完成的无效(操作)
    //动作是委托,所以我们只检查委托。
    if(!methods.Any(x=>x.Name==WellKnownMemberNames.OnCompleted&&x.ReturnsVoid&&x.Parameters.Length==1&&x.Parameters.First().Type.TypeKind==TypeKind.Delegate))
    {
    返回false;
    }
    //void GetResult()| | T GetResult()
    返回方法.Any(m=>m.Name==WellKnownMemberNames.GetResult&&!m.Parameters.Any());
    }
    
    Roslyn有一个内部接口,可以完全满足您的需求

    您可以复制它:

        /// <summary>
        /// If the <paramref name="symbol"/> is a method symbol, returns <see langword="true"/> if the method's return type is "awaitable", but not if it's <see langword="dynamic"/>.
        /// If the <paramref name="symbol"/> is a type symbol, returns <see langword="true"/> if that type is "awaitable".
        /// An "awaitable" is any type that exposes a GetAwaiter method which returns a valid "awaiter". This GetAwaiter method may be an instance method or an extension method.
        /// </summary>
        public static bool IsAwaitableNonDynamic(this ISymbol symbol, SemanticModel semanticModel, int position)
        {
            IMethodSymbol methodSymbol = symbol as IMethodSymbol;
            ITypeSymbol typeSymbol = null;
    
            if (methodSymbol == null)
            {
                typeSymbol = symbol as ITypeSymbol;
                if (typeSymbol == null)
                {
                    return false;
                }
            }
            else
            {
                if (methodSymbol.ReturnType == null)
                {
                    return false;
                }
            }
    
            // otherwise: needs valid GetAwaiter
            var potentialGetAwaiters = semanticModel.LookupSymbols(position,
                                                                   container: typeSymbol ?? methodSymbol.ReturnType.OriginalDefinition,
                                                                   name: WellKnownMemberNames.GetAwaiter,
                                                                   includeReducedExtensionMethods: true);
            var getAwaiters = potentialGetAwaiters.OfType<IMethodSymbol>().Where(x => !x.Parameters.Any());
            return getAwaiters.Any(VerifyGetAwaiter);
        }
    
        private static bool VerifyGetAwaiter(IMethodSymbol getAwaiter)
        {
            var returnType = getAwaiter.ReturnType;
            if (returnType == null)
            {
                return false;
            }
    
            // bool IsCompleted { get }
            if (!returnType.GetMembers().OfType<IPropertySymbol>().Any(p => p.Name == WellKnownMemberNames.IsCompleted && p.Type.SpecialType == SpecialType.System_Boolean && p.GetMethod != null))
            {
                return false;
            }
    
            var methods = returnType.GetMembers().OfType<IMethodSymbol>();
    
            // NOTE: (vladres) The current version of C# Spec, §7.7.7.3 'Runtime evaluation of await expressions', requires that
            // NOTE: the interface method INotifyCompletion.OnCompleted or ICriticalNotifyCompletion.UnsafeOnCompleted is invoked
            // NOTE: (rather than any OnCompleted method conforming to a certain pattern).
            // NOTE: Should this code be updated to match the spec?
    
            // void OnCompleted(Action) 
            // Actions are delegates, so we'll just check for delegates.
            if (!methods.Any(x => x.Name == WellKnownMemberNames.OnCompleted && x.ReturnsVoid && x.Parameters.Length == 1 && x.Parameters.First().Type.TypeKind == TypeKind.Delegate))
            {
                return false;
            }
    
            // void GetResult() || T GetResult()
            return methods.Any(m => m.Name == WellKnownMemberNames.GetResult && !m.Parameters.Any());
        }
    
    //
    ///如果是方法符号,则在方法的返回类型为“可等待”时返回,但在方法的返回类型为“可等待”时不返回。
    ///如果是类型符号,则返回该类型是否为“可等待”。
    ///“等待”的