在将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<int> DoSomethingAsync(); }
public class Callee: ICallee
{
public async Task<int> 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.
}
}
}
Run Code Online (Sandbox Code Playgroud)
Yko*_*kok 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);
}
}
}
}
}
/// <summary>
/// Checks if the <paramref name="typeSymbol"/> is one of the types specified
/// </summary>
/// <param name="typeSymbol"></param>
/// <param name="type"></param>
/// <returns></returns>
/// <remarks>This method should probably be rewritten so it doesn't merely compare the names, but instead the actual type.</remarks>
private static bool EqualsType(ITypeSymbol typeSymbol, params Type[] type)
{
var fullSymbolNameWithoutGeneric = $"{typeSymbol.ContainingNamespace.ToDisplayString()}.{typeSymbol.Name}";
return type.Any(x => fullSymbolNameWithoutGeneric.Equals(x.FullName));
}
Run Code Online (Sandbox Code Playgroud)
编译器将发出警告CS4014但仅在调用方法时才会发出警告async.
没有警告:
Task CallingMethod() {
DoWhateverAsync();
// More code that eventually returns a task.
}
Run Code Online (Sandbox Code Playgroud)
警告CS4014:由于未等待此调用,因此在完成调用之前,将继续执行当前方法.考虑将'await'运算符应用于调用的结果.
async Task CallingMethod() {
DoWhateverAsync();
}
Run Code Online (Sandbox Code Playgroud)
这在您的特定情况下并不是非常有用,因为您必须找到DoWhateverAsync调用的所有位置并更改它们以获取警告然后修复代码.但是您想首先使用编译器警告来查找这些调用.
我建议您使用Visual Studio查找所有用法DoWhateverAsync.无论如何,您必须通过编译器警告或通过使用列表来修改周围的代码.
最后,我们使用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);
}
Run Code Online (Sandbox Code Playgroud)
您有几种选择:
(?<!await|task(.*))\s([_a-zA-Z0-9\.])*Async\(假定您发布了固定的所有带有Async关键字的async方法,该方法的调用位于一行中。如果不正确,则不要使用它(或将缺少的验证添加到表达式中)。
如何使用罗斯林:

您可以复制粘贴以前的解决方案。创建
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class AsyncAwaitAnalyzer : DiagnosticAnalyzer
{ ...
}
Run Code Online (Sandbox Code Playgroud)
具有逻辑的类,以检测问题。并创造
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AsyncAwaitCodeFixProvider)), Shared]
public class AsyncAwaitCodeFixProvider : CodeFixProvider
{ ...
}
Run Code Online (Sandbox Code Playgroud)
提供解决问题的建议(添加等待)的类。
成功构建后,您将获得自己的.wsix软件包,可以将其安装到VS实例,并且在VS重新启动后应该会开始处理问题。
| 归档时间: |
|
| 查看次数: |
4149 次 |
| 最近记录: |