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;
}
}