当前位置:  开发笔记 > 编程语言 > 正文

查找无休止的异步方法调用

如何解决《查找无休止的异步方法调用》经验,为你挑选了4个好方法。

在将ASP.NET应用程序迁移到async/await模型时,我偶然发现了一个相当危险的情况.

情况是我创建了一个异步方法:async Task DoWhateverAsync(),将接口中的声明更改为,Task DoWhateverAsync()并希望编译器通过该警告告诉我代码现在在哪里是错误的.好吧,运气不好.只要通过接口注入该对象,就不会发出警告.:-(

这很危险.有没有办法自动检查返回任务的非等待方法?我不介意一些警告太多,但我不想错过一个.

这是一个例子:

using System.Threading.Tasks;
namespace AsyncAwaitGames
{
    // In my real case, that method just returns Task.
    public interface ICallee { Task DoSomethingAsync(); }

    public class Callee: ICallee
    {
        public async Task DoSomethingAsync() => await Task.FromResult(0);
    }
    public class Caller
    {
        public void DoCall()
        {
            ICallee xxx = new Callee();

            // In my real case, the method just returns Task,
            // so there is no type mismatch when assigning a result 
            // either.
            xxx.DoSomethingAsync(); // This is where I had hoped for a warning.
        }
    }
}

Ykok.. 14

在遇到这个问题相当困难之后,我决定创建一个带有代码修复的Analyzer来解决它.

代码可在此处获得:https: //github.com/ykoksen/unused-task-warning

它也可以作为NuGet包用作项目的分析器(当它构建时):https: //www.nuget.org/packages/Lindhart.Analyser.MissingAwaitWarning/#

此外,它还可用作Visual Studio Extension(2017年).但是,这仅分析当前打开的文件,因此我建议使用NuGet包.扩展可在此处获得(或在Visual Studio中搜索):https: //marketplace.visualstudio.com/items?itemName = Lindhart.missingAwaitWarning #overview

分析器的代码:

    public override void Initialize(AnalysisContext context)
    {
        context.RegisterSyntaxNodeAction(AnalyseSymbolNode, SyntaxKind.InvocationExpression);
    }

    private void AnalyseSymbolNode(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext)
    {
        if (syntaxNodeAnalysisContext.Node is InvocationExpressionSyntax node)
        {
            if (syntaxNodeAnalysisContext
                    .SemanticModel
                    .GetSymbolInfo(node.Expression, syntaxNodeAnalysisContext.CancellationToken)
                    .Symbol is IMethodSymbol methodSymbol)
            {
                if (node.Parent is ExpressionStatementSyntax)
                {
                    // Only checks for the two most common awaitable types. In principle this should instead check all types that are awaitable
                    if (EqualsType(methodSymbol.ReturnType, typeof(Task), typeof(ConfiguredTaskAwaitable)))
                    {
                        var diagnostic = Diagnostic.Create(Rule, node.GetLocation(), methodSymbol.ToDisplayString());

                        syntaxNodeAnalysisContext.ReportDiagnostic(diagnostic);
                    }
                }
            }
        }
    }

    /// 
    /// Checks if the  is one of the types specified
    /// 
    /// 
    /// 
    /// 
    /// This method should probably be rewritten so it doesn't merely compare the names, but instead the actual type.
    private static bool EqualsType(ITypeSymbol typeSymbol, params Type[] type)
    {
        var fullSymbolNameWithoutGeneric = $"{typeSymbol.ContainingNamespace.ToDisplayString()}.{typeSymbol.Name}";
        return type.Any(x => fullSymbolNameWithoutGeneric.Equals(x.FullName));
    }


Martin Liver.. 7

编译器将发出警告CS4014但仅在调用方法时才会发出警告async.

没有警告:

Task CallingMethod() {
    DoWhateverAsync();
    // More code that eventually returns a task.
}

警告CS4014:由于未等待此调用,因此在完成调用之前,将继续执行当前方法.考虑将'await'运算符应用于调用的结果.

async Task CallingMethod() {
    DoWhateverAsync();
}

这在您的特定情况下并不是非常有用,因为您必须找到DoWhateverAsync调用的所有位置并更改它们以获取警告然后修复代码.但是您想首先使用编译器警告来查找这些调用.

我建议您使用Visual Studio查找所有用法DoWhateverAsync.无论如何,您必须通过编译器警告或通过使用列表来修改周围的代码.



1> Ykok..:

在遇到这个问题相当困难之后,我决定创建一个带有代码修复的Analyzer来解决它.

代码可在此处获得:https: //github.com/ykoksen/unused-task-warning

它也可以作为NuGet包用作项目的分析器(当它构建时):https: //www.nuget.org/packages/Lindhart.Analyser.MissingAwaitWarning/#

此外,它还可用作Visual Studio Extension(2017年).但是,这仅分析当前打开的文件,因此我建议使用NuGet包.扩展可在此处获得(或在Visual Studio中搜索):https: //marketplace.visualstudio.com/items?itemName = Lindhart.missingAwaitWarning #overview

分析器的代码:

    public override void Initialize(AnalysisContext context)
    {
        context.RegisterSyntaxNodeAction(AnalyseSymbolNode, SyntaxKind.InvocationExpression);
    }

    private void AnalyseSymbolNode(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext)
    {
        if (syntaxNodeAnalysisContext.Node is InvocationExpressionSyntax node)
        {
            if (syntaxNodeAnalysisContext
                    .SemanticModel
                    .GetSymbolInfo(node.Expression, syntaxNodeAnalysisContext.CancellationToken)
                    .Symbol is IMethodSymbol methodSymbol)
            {
                if (node.Parent is ExpressionStatementSyntax)
                {
                    // Only checks for the two most common awaitable types. In principle this should instead check all types that are awaitable
                    if (EqualsType(methodSymbol.ReturnType, typeof(Task), typeof(ConfiguredTaskAwaitable)))
                    {
                        var diagnostic = Diagnostic.Create(Rule, node.GetLocation(), methodSymbol.ToDisplayString());

                        syntaxNodeAnalysisContext.ReportDiagnostic(diagnostic);
                    }
                }
            }
        }
    }

    /// 
    /// Checks if the  is one of the types specified
    /// 
    /// 
    /// 
    /// 
    /// This method should probably be rewritten so it doesn't merely compare the names, but instead the actual type.
    private static bool EqualsType(ITypeSymbol typeSymbol, params Type[] type)
    {
        var fullSymbolNameWithoutGeneric = $"{typeSymbol.ContainingNamespace.ToDisplayString()}.{typeSymbol.Name}";
        return type.Any(x => fullSymbolNameWithoutGeneric.Equals(x.FullName));
    }



2> Martin Liver..:

编译器将发出警告CS4014但仅在调用方法时才会发出警告async.

没有警告:

Task CallingMethod() {
    DoWhateverAsync();
    // More code that eventually returns a task.
}

警告CS4014:由于未等待此调用,因此在完成调用之前,将继续执行当前方法.考虑将'await'运算符应用于调用的结果.

async Task CallingMethod() {
    DoWhateverAsync();
}

这在您的特定情况下并不是非常有用,因为您必须找到DoWhateverAsync调用的所有位置并更改它们以获取警告然后修复代码.但是您想首先使用编译器警告来查找这些调用.

我建议您使用Visual Studio查找所有用法DoWhateverAsync.无论如何,您必须通过编译器警告或通过使用列表来修改周围的代码.


@David不,在OP的情况下*调用方法不是'async`*因为他正处于从同步模型转换到异步模型的过程中.

3> Volker..:

最后,我们使用roslyn查找忽略Task或Task <>返回值的所有实例:

if (methodSymbol.ReturnType.Equals(syntaxNodeAnalysisContext.SemanticModel.Compilation.GetTypeByMetadataName(typeof(Task).FullName)))
{
    // For all such symbols, produce a diagnostic.
    var diagnostic = Diagnostic.Create(Rule, node.GetLocation(), methodSymbol.ToDisplayString());

    syntaxNodeAnalysisContext.ReportDiagnostic(diagnostic);
}
if (((INamedTypeSymbol) methodSymbol.ReturnType).IsGenericType && ((INamedTypeSymbol) methodSymbol.ReturnType).BaseType.Equals(syntaxNodeAnalysisContext.SemanticModel.Compilation.GetTypeByMetadataName(typeof(Task).FullName)))
{
    // For all such symbols, produce a diagnostic.
    var diagnostic = Diagnostic.Create(Rule, node.GetLocation(), methodSymbol.ToDisplayString());

    syntaxNodeAnalysisContext.ReportDiagnostic(diagnostic);
}


我请求您根据您分享的建议添加一些背景/评论.它将帮助提问者和其他未来的读者更好地理解你的帖子.

4> Major..:

您有几种选择:

这是使用“整个”解决方案中内置的VS搜索功能(CTRL + SHIFT + F)搜索的最简单的“ Caveman”解决方案,也在“ 查找选项”下,单击“ 使用正则表达式 ”复选框 并使用此正则表达式:(?假定您发布了固定的所有带有Async关键字的async方法,该方法的调用位于一行中。如果不正确,则不要使用它(或将缺少的验证添加到表达式中)。

使用一些第三方代码分析器工具Nuget程序包。ReSharper非常受欢迎,我相信它可以检测到此问题,或者您可以创建自己的规则。

我的选择是使用Roslyn(@Volker提供了一种解决方案)。您可以使用代码修复解决方案创建自己的规则集(灯泡图标将显示您的代码修复),因此这是最好的。

更新: VS 2019默认情况下检查此问题并给出警告。

如何使用罗斯林:

您必须从此处安装.NET Compiler Platform SDK:

使用VS 2017版本15.2(或更高版本)

创建一个新项目File-> New- > Project,在Extensibility组下,选择:带代码修复的分析器(Nuget + VSIX)您必须以.NET Framework 4.6.2为目标来创建此项目。

您可以复制粘贴以前的解决方案。创建

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class AsyncAwaitAnalyzer : DiagnosticAnalyzer
{ ...
}

具有逻辑的类,以检测问题。并创造

[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AsyncAwaitCodeFixProvider)), Shared]
public class AsyncAwaitCodeFixProvider : CodeFixProvider
{ ...
}

提供解决问题的建议(添加等待)的类。

成功构建后,您将获得自己的.wsix软件包,可以将其安装到VS实例,并且在VS重新启动后应该会开始处理问题。

推荐阅读
重庆制造漫画社
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有