Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/296.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何处理异步;使用;陈述陷阱?_C#_Async Await_Using - Fatal编程技术网

C# 如何处理异步;使用;陈述陷阱?

C# 如何处理异步;使用;陈述陷阱?,c#,async-await,using,C#,Async Await,Using,可以在“使用”语句中使用异步方法的结果,例如: using (await fooAsync()) { ... } 不幸的是,很容易犯这样的错误: using (fooAsync()) { ... } 一旦你犯了这个错误,就很难发现。如果您返回的任务恰好已完成,则Task.Dispose将成功完成。结果还表明,您实际上想要用“using”语句保护的对象被挂起 作为fooAsync的作者,防止错误被默默忽略的最佳方法是什么?您必须小心 如果您没有在正常代码中等待任务,Visual

可以在“使用”语句中使用异步方法的结果,例如:

using (await fooAsync())
{
    ...
}
不幸的是,很容易犯这样的错误:

using (fooAsync())
{
    ...
}
一旦你犯了这个错误,就很难发现。如果您返回的任务恰好已完成,则Task.Dispose将成功完成。结果还表明,您实际上想要用“using”语句保护的对象被挂起


作为fooAsync的作者,防止错误被默默忽略的最佳方法是什么?

您必须小心

如果您没有在正常代码中等待
任务
,VisualStudio通常会发出警告,但如果它是
使用
的主题,则不会发出警告。例如:

请注意,使用行时,
上缺少波形

正如您所提到的,确实实现了
IDisposable
,因此这是完全有效的代码,这使得很难说这是否真的是一个bug。你可以随时开始讨论

也就是说,如果您查看
Task.Dispose()
的代码,如果它在完成之前就被释放了,那么它就会被释放

//必须完成任务才能处置
如果(!已完成)
{
抛出新的InvalidOperationException(Environment.GetResourceString(“Task\u Dispose\u NotCompleted”);
}
因此,虽然不能保证,但如果您忘记等待,则很有可能在测试时遇到该异常

只有在使用
时不在
中使用一次性对象,这才是真正的问题,这是非常罕见的。否则,您会很快发现:

使用(var foo=fooancy()){
foo.WhyDoesNothingWork();
}
但是,正如您所指出的,在块内不使用一次性对象的情况下,一个非常合理的用法是异步锁的实现,如:

使用(wait _mutex.LockAsync()){
//做事
}
原来Stephen Cleary创建了一个类来解决这个问题。请参见该课程顶部的注释:

围绕任务的一个可等待的包装器,其结果是一次性的。包装器不是一次性的,因此当适当的用法应该是“using(wait MyAsync())”时,这可以防止诸如“using(MyAsync())”之类的使用错误


我在这里找到了相同的另一个实现:

如果您想使用Roslyn Analyzer方法,以下内容应该足以让您开始。这将检查using块中是否有任何不具有等待的调用,确定它们是否为泛型任务,并检查它们的泛型任务参数是否为IDisposable或实现IDisposable

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class AsyncUsingAnalyzer : DiagnosticAnalyzer
{
    public const string DiagnosticId = "UnAwaitedTaskInUsing";

    private const string Title = "Await Async Method";
    private const string MessageFormat = "{0} should be awaited";
    private const string Description = "Detected un-awaited task in using.";
    private const string Category = "Usage";

    private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);

    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }

    public override void Initialize(AnalysisContext context)
    {
        context.RegisterSyntaxNodeAction(AnalyzeSymbol, SyntaxKind.UsingStatement);
    }

    private static void AnalyzeSymbol(SyntaxNodeAnalysisContext context)
    {
        var invocationExpression = context.Node.ChildNodes().OfType<InvocationExpressionSyntax>().FirstOrDefault();
        if(invocationExpression == null) return;

        var awaitKeyword = context.Node.ChildTokens().OfType<AwaitExpressionSyntax>().FirstOrDefault();
        if(awaitKeyword != null) return;

        var symbolInfo = context.SemanticModel.GetSymbolInfo(invocationExpression).Symbol as IMethodSymbol;
        if(symbolInfo == null) return;

        var genericType = (symbolInfo.ReturnType as INamedTypeSymbol);
        if (!genericType?.IsGenericType ?? false || genericType.Name.ToString() != "Task'1")
            return;

        var genericTypeParameter = genericType.TypeArguments.FirstOrDefault();
        if(genericTypeParameter == null)
            return;

        var disposable = context.Compilation.GetTypeByMetadataName("System.IDisposable");
        if(!disposable.Equals(genericTypeParameter) && !genericTypeParameter.Interfaces.Any(x => disposable.Equals(x)))
            return;

        var diagnostic = Diagnostic.Create(Rule, invocationExpression.GetLocation(), invocationExpression);
        context.ReportDiagnostic(diagnostic);
    }
}
[DiagnosticanAnalyzer(LanguageNames.CSharp)]
公共类AsyncUsingAnalyzer:DiagnosticanAnalyzer
{
public const string DiagnosticId=“UnAwaitedTaskInUsing”;
private const string Title=“等待异步方法”;
private const string MessageFormat=“{0}应等待”;
private const string Description=“在使用中检测到未等待的任务。”;
private const string Category=“用法”;
私有静态DiagnosticDescriptor规则=新的DiagnosticDescriptor(DiagnosticId、标题、消息格式、类别、DiagnosticSeverity.Warning、IsEnabled默认为true,描述为description);
公共覆盖ImmutableArray支持的诊断{get{return ImmutableArray.Create(Rule);}
公共覆盖无效初始化(AnalysisContext上下文)
{
RegisterSyntaxNodeAction(AnalyzeSymbol,SyntaxKind.UsingStatement);
}
专用静态无效分析符号(SyntaxNodeAnysisContext上下文)
{
var invocationExpression=context.Node.ChildNodes().OfType().FirstOrDefault();
if(invocationExpression==null)返回;
var waitkeyword=context.Node.ChildTokens().OfType().FirstOrDefault();
如果(关键字!=null)返回;
var symbolInfo=context.SemanticModel.GetSymbolInfo(调用表达式).Symbol作为IMethodSymbol;
if(symbolInfo==null)返回;
变量genericType=(symbolInfo.ReturnType为INamedTypeSymbol);
如果(!genericType?.IsGenericType??false | | genericType.Name.ToString()!=“任务'1”)
返回;
var genericTypeParameter=genericType.TypeArguments.FirstOrDefault();
if(genericTypeParameter==null)
返回;
var disposable=context.Compilation.GetTypeByMetadataName(“System.IDisposable”);
如果(!disposable.Equals(genericTypeParameter)&&!genericTypeParameter.Interfaces.Any(x=>disposable.Equals(x)))
返回;
var diagnostic=diagnostic.Create(规则,invocationExpression.GetLocation(),invocationExpression);
上下文。报告诊断(诊断);
}
}
目前,严重性设置为“警告”,但如果有人忘记等待任务,您可以很容易地将其设置为编译时抛出的错误


我不认为这是一个完整的解决方案,但它应该足以让你开始一个。

一个ROSLYN代码分析器可以检测到这一点,我认为一个简单的方法来避免这是依赖于<代码> IASYNC一次性使用< /Code >和<代码>等待使用< /Code >,它不能使用
块语法与<代码>混淆。显然,作为API作者,这不是你可以单方面决定做的事情。有一个一次性类型,你在使用块中不使用它的任何成员,这很奇怪,这是唯一一次发生这种情况。@Fax这是有效的。那是图书馆的吗?我刚刚用库测试了一下,结果发现@StephenCleary创建了一个类来解决这个问题(请参见该类顶部的注释)。@Fax我将其添加到下面的答案中。IsCompleted检查的主要问题是API的第一个版本可能使用Task.FromResult,而下一个版本并不有趣。我想这必须添加到从程序集使用FooAsync的每个代码库中?@Fax是的,它需要