如何在 .NET Core 中正确使用代码契约 [英] How to properly use Code Contracts in .NET Core
问题描述
我想知道如何在 .NET Core 中正确使用代码契约,到目前为止我尝试将 CC 添加到我的项目中,编译和调试.我对使用 Contract.Requires
的每个调用中出现的消息以及通过谷歌搜索找到的信息感到困惑.
I wonder, how to properly use Code Contracts in .NET Core, so far I tried to add CC to my project, compile and debug. I'm confused by message, which is appearing in each call which uses Contract.Requires
, and information found by googling.
消息指出:
必须使用代码契约二进制重写器 (CCRewrite) 重写程序集,因为它正在调用 Contract.Requires
并且定义了 CONTRACTS_FULL
符号.从您的项目中删除 CONTRACTS_FULL
符号的任何显式定义并重新构建.CCRewrite ....
An assembly must be rewritten using the code contracts binary rewriter (CCRewrite) because it is calling
Contract.Requires<TException>
andCONTRACTS_FULL
symbol is defined. Remove any explicit definitions of theCONTRACTS_FULL
symbol from your project and rebuild. CCRewrite ....
正如我所见,项目属性中没有 CC 选项,而且我所看到的 CC 的 Github 存储库几乎已死.在 .NET Core 中成功使用 CC 有什么方法吗?
As I can see there are no CC options in the project properties and as I can see CC's Github repository is nearly dead. Is the are any way how to successfully use CC in .NET Core?
如果没有,有什么简单的方法可以替换它们吗?我使用 Contract.Requires
和 ContractClassAttribute
.替换 Contract.Requires
是显而易见的,但是 ContractClassAttribute 让我大吃一惊:-)
And if not, is there any simple way how to replace them? I use Contract.Requires
and ContractClassAttribute
. Replacing Contract.Requires
is obvious, but ContractClassAttribute is blowing my mind :-)
推荐答案
首先,让我们了解什么是CodeContracts,根据微软文档:
First of all, let's understand what CodeContracts are, according to microsoft docs:
代码契约提供了一种指定前置条件、后置条件、和代码中的对象不变量.先决条件是要求输入方法或属性时必须满足.后置条件描述方法或属性代码退出时的期望.对象不变量描述了一个类的预期状态状态良好.
Code contracts provide a way to specify preconditions, postconditions, and object invariants in your code. Preconditions are requirements that must be met when entering a method or property. Postconditions describe expectations at the time the method or property code exits. Object invariants describe the expected state for a class that is in a good state.
意思是,为了让事情变得简单,CodeContracts 帮助我们简化代码中的测试.
Meaning, to make things simple, CodeContracts help us simplify tests in our code.
我们如何使用代码契约?
考虑这个例子:
if ( x == null ) throw new ...
Contract.EndContractBlock(); // All previous "if" checks are preconditions
先决条件两种情况之一是什么意思?
What does it mean by preconditions one of two cases?
- 语句出现在方法中的任何其他语句之前.
- 整套此类声明后跟一个明确的Contract 方法调用,例如对 需要,确保、EnsuresOnThrow 或 EndContractBlock 方法.
- The statements appear before any other statements in a method.
- The entire set of such statements is followed by an explicit Contract method call, such as a call to the Requires, Ensures, EnsuresOnThrow, or EndContractBlock method.
当 if-then-throw
语句以这种形式出现时,工具会将它们识别为遗留的 requires 语句.如果没有其他合约遵循 if-then-throw 序列,请使用 Contract.EndContractBlock 方法.
When if-then-throw
statements appear in this form, the tools recognize them as legacy requires statements. If no other contracts follow the if-then-throw sequence, end the code with the Contract.EndContractBlock method.
您也可以在后置条件中使用它:
什么是后置条件?
后置条件是方法状态的契约,当它终止.在退出方法之前检查后置条件.失败后置条件的运行时行为由运行时分析器.
Postconditions are contracts for the state of a method when it terminates. The postcondition is checked just before exiting a method. The run-time behavior of failed postconditions is determined by the runtime analyzer.
与前置条件不同,后置条件可以引用更少的成员能见度.客户可能无法理解或使用某些使用私有状态由后置条件表示的信息,但这并不影响客户使用该方法的能力正确.
Unlike preconditions, postconditions may reference members with less visibility. A client may not be able to understand or make use of some of the information expressed by a postcondition using private state, but this does not affect the client's ability to use the method correctly.
意思是,简而言之,后置条件帮助我们测试我们的方法.
Meaning, to make things short, Postconditions help us test our methods.
一个例子是:
Contract.Ensures( this.F > 0 );
请注意特殊的postcontions:
Please note special postcontions:
- 您可以使用表达式
Contract.Result
在后置条件中引用方法返回值,其中() T
替换为方法的返回类型.当编译器无法推断类型时,您必须显式提供它. - 后置条件中的前置值是指方法或属性开头的表达式的值.它使用表达式
Contract.OldValue
,其中(e) T
是e
的类型.只要编译器能够推断其类型,就可以省略泛型类型参数.(例如,C# 编译器总是推断类型,因为它接受一个参数.)对于 e 中可能出现的内容以及旧表达式可能出现的上下文有几个限制.旧表达式不能包含另一个旧表达式.最重要的是,旧表达式必须引用存在于方法前提条件中的值.换句话说,它必须是一个表达式,只要该方法的前提条件为真,就可以对其进行求值.
- You can refer to method return values in postconditions by using the expression
Contract.Result<T>()
, whereT
is replaced by the return type of the method. When the compiler is unable to infer the type, you must explicitly provide it. - A prestate value in a postcondition refers to the value of an expression at the start of a method or property. It uses the expression
Contract.OldValue<T>(e)
, whereT
is the type ofe
. You can omit the generic type argument whenever the compiler is able to infer its type. (For example, the C# compiler always infers the type because it takes an argument.) There are several restrictions on what can occur in e and the contexts in which an old expression may appear. An old expression cannot contain another old expression. Most importantly, an old expression must refer to a value that existed in the method's precondition state. In other words, it must be an expression that can be evaluated as long as the method's precondition is true.
最后你有不变量:
对象不变量是每个实例都应该为真的条件只要该对象对客户端可见,就属于一个类.他们表示对象被认为是正确的条件.
Object invariants are conditions that should be true for each instance of a class whenever that object is visible to a client. They express the conditions under which the object is considered to be correct.
意思是,不变量有助于测试我们的类代码和实例.
Meaning, Invariants help test our class code and instances.
一个例子是:
[ContractInvariantMethod]
protected void ObjectInvariant ()
{
Contract.Invariant(this.y >= 0);
Contract.Invariant(this.x > this.y);
...
}
<小时>
正确使用CodeContracts的完整示例:
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net.Http.Headers;
using System.Diagnostics.Contracts;
namespace System.Net.Http
{
public class FormUrlEncodedContent : ByteArrayContent
{
public FormUrlEncodedContent(IEnumerable<KeyValuePair<string, string>> nameValueCollection)
: base(GetContentByteArray(nameValueCollection))
{
Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
}
private static byte[] GetContentByteArray(IEnumerable<KeyValuePair<string, string>> nameValueCollection)
{
if (nameValueCollection == null)
{
throw new ArgumentNullException(nameof(nameValueCollection));
}
Contract.EndContractBlock();
// Encode and concatenate data
StringBuilder builder = new StringBuilder();
foreach (KeyValuePair<string, string> pair in nameValueCollection)
{
if (builder.Length > 0)
{
builder.Append('&');
}
builder.Append(Encode(pair.Key));
builder.Append('=');
builder.Append(Encode(pair.Value));
}
return HttpRuleParser.DefaultHttpEncoding.GetBytes(builder.ToString());
}
private static string Encode(string data)
{
if (String.IsNullOrEmpty(data))
{
return String.Empty;
}
// Escape spaces as '+'.
return Uri.EscapeDataString(data).Replace("%20", "+");
}
internal override Stream TryCreateContentReadStream() =>
GetType() == typeof(FormUrlEncodedContent) ? CreateMemoryStreamForByteArray() : // type check ensures we use possible derived type's CreateContentReadStreamAsync override
null;
}
}
这篇关于如何在 .NET Core 中正确使用代码契约的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!