C# 代码契约和异步

C# 代码契约和异步,c#,.net,task-parallel-library,code-contracts,async-ctp,C#,.net,Task Parallel Library,Code Contracts,Async Ctp,向返回任务的异步方法添加后置条件的推荐方法是什么 我已阅读以下建议: 这篇文章建议将每个方法实现为同步的、收缩的,然后将异步的对应物实现为一个简单的包装器。不幸的是,我不认为这是一个可行的解决方案(可能是因为我自己的误解): 异步方法虽然被假定为sync方法的包装器,但没有任何真正的代码契约,因此可以按照自己的意愿执行 致力于异步的代码库不太可能实现所有内容的同步对应。因此,在其他异步方法上实现包含awaits的新方法就必须是异步的。这些方法本质上是异步的,不容易转换为同步的。它们不仅仅是包装

向返回
任务的异步方法添加后置条件的推荐方法是什么

我已阅读以下建议:

这篇文章建议将每个方法实现为同步的、收缩的,然后将异步的对应物实现为一个简单的包装器。不幸的是,我不认为这是一个可行的解决方案(可能是因为我自己的误解):

  • 异步方法虽然被假定为sync方法的包装器,但没有任何真正的代码契约,因此可以按照自己的意愿执行
  • 致力于异步的代码库不太可能实现所有内容的同步对应。因此,在其他异步方法上实现包含
    await
    s的新方法就必须是异步的。这些方法本质上是异步的,不容易转换为同步的。它们不仅仅是包装纸
  • 即使我们说我们可以使用
    .Result
    .Wait()
    而不是
    Wait
    (这实际上会导致一些
    SyncContext
    死锁,并且无论如何都必须用异步方法重新编写),我仍然相信第一点


    关于代码契约和第三方物流,有没有其他的想法,或者我有没有遗漏什么?

    我已经向异步团队指出了这一点,正如其他人所做的那样。目前,契约和异步(几乎)是互斥的。所以,至少微软的一些人意识到了这个问题,但我不知道他们打算怎么做

    我不建议将异步方法编写为同步方法的包装器。事实上,我倾向于做相反的事情

    先决条件可以奏效。我最近没有试过;您可能需要一个包含前提条件的小包装器来包装异步方法

    后置条件几乎被打破了

    断言和假设确实正常工作,但是静态检查器实际上是有限的,因为后置条件被破坏了

    在异步世界中,不变量没有那么大意义,在异步世界中,可变状态往往会成为阻碍。(Async轻轻地将您从OOP推向功能风格)

    希望在VSVNEXT中,契约将使用异步感知的后置条件进行更新,这也将使静态检查器能够更好地处理异步方法中的断言

    同时,您可以通过编写一个假设来模拟后条件:

    // Synchronous version for comparison.
    public static string Reverse(string s)
    {
      Contract.Requires(s != null);
      Contract.Ensures(Contract.Result<string>() != null);
    
      return ...;
    }
    
    // First wrapper takes care of preconditions (synchronously).
    public static Task<string> ReverseAsync(string s)
    {
      Contract.Requires(s != null);
    
      return ReverseWithPostconditionAsync(s);
    }
    
    // Second wrapper takes care of postconditions (asynchronously).
    private static async Task<string> ReverseWithPostconditionAsync(string s)
    {
      var result = await ReverseImplAsync(s);
    
      // Check our "postcondition"
      Contract.Assume(result != null);
    
      return result;
    }
    
    private static async Task<string> ReverseImplAsync(string s)
    {
      return ...;
    }
    
    //用于比较的同步版本。
    公共静态字符串反转(字符串s)
    {
    合同。要求(s!=null);
    Contract.sure(Contract.Result()!=null);
    返回。。。;
    }
    //第一个包装器负责先决条件(同步)。
    公共静态任务反向同步(字符串s)
    {
    合同。要求(s!=null);
    返回ReverseWithPostconditionAsync(s);
    }
    //第二个包装器负责后置条件(异步)。
    专用静态异步任务ReverseWithPostconditionAsync(字符串s)
    {
    var结果=等待反向同步;
    //检查我们的“后条件”
    合同。假设(结果!=null);
    返回结果;
    }
    私有静态异步任务反向同步(字符串s)
    {
    返回。。。;
    }
    
    代码契约的某些用法是不可能的——例如,在接口或基类的异步成员上指定后置条件


    就我个人而言,我刚刚在异步代码中完全避免了合同,希望微软能在几个月内修复它。

    键入了这个,但忘了点击“Post”…)

    目前还没有专门的支持。最好是这样(不要使用
    async
    关键字,但想法相同-在异步CTP下,重写器可能会以不同的方式工作,我还没有尝试过):

    公共静态任务Do()
    {
    Contract.sure(Contract.Result()!=null);
    Contract.Result(Contract.Result().Result>0);
    返回Task.Factory.StartNew(()=>{Thread.Sleep(3000);返回2;});
    }
    公共静态void Main(字符串[]args)
    {
    var x=Do();
    控制台写入线(“处理”);
    控制台写入线(x.Result);
    }
    
    但是,这意味着在任务完成评估之前,“异步”方法不会实际返回,因此在3秒钟之后才会打印“处理”。这类似于延迟返回
    IEnumerable
    s的方法的问题-契约必须枚举
    IEnumerable
    中的所有项,以确保条件成立,即使调用方不会实际使用所有项

    您可以通过将合同模式更改为“先决条件”
    ,来解决此问题,但这意味着实际上不会检查post条件

    静态检查器也无法将
    结果
    与lambda连接,因此您将收到一条“确保未经验证”消息。(通常,静态检查器不会证明lambda/delegate的情况。)


    我认为,为了获得对Tasks/await的适当支持,代码合同团队必须在访问结果字段时才添加前提条件检查的特殊情况任务。

    将新答案发布到这个旧线程,因为它是google返回的关于CodeContract和Async的问题的第一个答案

    目前,返回任务的异步方法的契约工作正常,无需避免它们

    异步方法的标准合同:

    [ContractClass(typeof(ContractClassForIFoo))]
    public interface IFoo
    {
        Task<object> MethodAsync();
    }
    
    
    [ContractClassFor(typeof(IFoo))]
    internal abstract class ContractClassForIFoo : IFoo
    {
        #region Implementation of IFoo
    
        public Task<object> MethodAsync()
        {
            Contract.Ensures(Contract.Result<Task<object>>() != null);
            Contract.Ensures(Contract.Result<Task<object>>().Status != TaskStatus.Created);
            Contract.Ensures(Contract.Result<object>() != null);
            throw new NotImplementedException();
        }
    
        #endregion
    }
    
    public class Foo : IFoo
    {
        public async Task<object> MethodAsync()
        {
            var result = await Task.FromResult(new object());
            return result;
        }
    }
    
    [ContractClass(typeof(ContractClassForIFoo))]
    公共接口IFoo
    {
    任务方法异步();
    }
    [ContractClassFor(typeof(IFoo))]
    内部抽象类ContractClassForIFoo:IFoo
    {
    #IFoo的区域实现
    公共任务方法异步()
    {
    Contract.sure(Contract.Result()!=null);
    Contract.survey(Contract.Result().Status!=TaskStatus.Created);
    Contract.sure(Contract.Result()!=null);
    抛出新的NotImplementedException();
    }
    #端区
    }
    公共类Foo:IFoo
    {
    酒吧
    
    [ContractClass(typeof(ContractClassForIFoo))]
    public interface IFoo
    {
        Task<object> MethodAsync();
    }
    
    
    [ContractClassFor(typeof(IFoo))]
    internal abstract class ContractClassForIFoo : IFoo
    {
        #region Implementation of IFoo
    
        public Task<object> MethodAsync()
        {
            Contract.Ensures(Contract.Result<Task<object>>() != null);
            Contract.Ensures(Contract.Result<Task<object>>().Status != TaskStatus.Created);
            Contract.Ensures(Contract.Result<object>() != null);
            throw new NotImplementedException();
        }
    
        #endregion
    }
    
    public class Foo : IFoo
    {
        public async Task<object> MethodAsync()
        {
            var result = await Task.FromResult(new object());
            return result;
        }
    }
    
    public static class ContractsAbbreviators
    {
        [ContractAbbreviator]
        public static void EnsureTaskIsStarted()
        {
            Contract.Ensures(Contract.Result<Task>() != null);
            Contract.Ensures(Contract.Result<Task>().Status != TaskStatus.Created);
        }
    
    }
    
    [ContractClass(typeof(ContractClassForIFoo))]
    public interface IFoo
    {
        Task<int> MethodAsync(int val);
    }
    
    [ContractClassFor(typeof(IFoo))]
    internal abstract class ContractClassForIFoo : IFoo
    {
        public Task<int> MethodAsync(int val)
        {
            Contract.Requires(val >= 0);
            ContractsAbbreviators.EnsureTaskIsStarted();
            Contract.Ensures(Contract.Result<int>() == val);
            Contract.Ensures(Contract.Result<int>() >= 5);
            Contract.Ensures(Contract.Result<int>() < 10);
            throw new NotImplementedException();
        }
    }
    
    public class FooContractFailTask : IFoo
    {
        public Task<int> MethodAsync(int val)
        {
            return new Task<int>(() => val);
            // esnure raises exception // Contract.Ensures(Contract.Result<Task>().Status != TaskStatus.Created); 
        }
    }
    
    public class FooContractFailTaskResult : IFoo
    {
        public async Task<int> MethodAsync(int val)
        {
            await Task.Delay(val).ConfigureAwait(false);
            return val + 1;
            // esnure raises exception // Contract.Ensures(Contract.Result<int>() == val);
        }
    }
    
    public class Foo : IFoo
    {
        public async Task<int> MethodAsync(int val)
        {
            const int maxDeapth = 9;
    
            await Task.Delay(val).ConfigureAwait(false);
    
            if (val < maxDeapth)
            {
                await MethodAsync(val + 1).ConfigureAwait(false);
            }
    
            return val;
        }
    }