如何在.NET Core中正确使用代码约定

Jiř*_*ček 26 c# code-contracts .net-core

我想知道,如何在.NET Core中正确使用Code Contracts ,到目前为止我试图将CC添加到我的项目中,进行编译和调试.我对每个使用的呼叫中出现的消息Contract.Requires以及通过Google搜索找到的信息感到困惑.

消息说明:

必须使用代码契约二进制重写器(CCRewrite)重写程序集,因为它正在调用Contract.Requires<TException>并且CONTRACTS_FULL符号已定义.CONTRACTS_FULL从项目中删除符号的任何显式定义并重建.CCRewrite ....

正如我所看到的,项目属性中没有CC选项,因为我可以看到CC的Github存储库几乎已经死了.是如何在.NET Core中成功使用CC的?

如果没有,是否有任何简单的方法来替换它们?我用Contract.RequiresContractClassAttribute.更换Contract.Requires是显而易见的,但ContractClassAttribute让我大吃一惊:-)

Dar*_*iak 10

答案是:.NET Core 不再支持代码契约。它在 .NET Core 2.0 中可用,但不再维护

\n

请参阅文档页面的官方声明:

\n
\n

笔记

\n

.NET 5+(包括 .NET Core 版本)不支持代码协定。考虑改用可为Null 的引用类型

\n
\n

同样在 Github 线程上(.NET Core 今后将支持代码契约吗?#6361):

\n
\n

我们不久前就停止了对代码合约的投资。作为兼容性工作的一部分,我们已将其添加到 .NET Core 2.0,但我不建议新代码使用它。

\n

FWIW,我们已经查看了许多使用它们的代码库,并且似乎压倒性的使用与空处理有关。C# 8 的可空引用类型正在对此进行替代。

\n

\xe2\x80\x94 Immo Landwerth,Microsoft .NET 团队的项目经理。https://twitter.com/terrajobst

\n
\n


Bar*_*r J 6

首先,让我们了解什么是CodeContracts,根据microsoft docs

代码契约提供了一种在代码中指定前置条件、后置条件和对象不变量的方法。先决条件是输入方法或属性时必须满足的要求。后置条件描述了方法或属性代码退出时的期望。对象不变量描述处于良好状态的类的预期状态。

意思是,为了让事情变得简单,CodeContracts 帮助我们简化代码中的测试。

我们如何使用代码契约?

考虑这个例子:

if ( x == null ) throw new ...  
Contract.EndContractBlock(); // All previous "if" checks are preconditions  
Run Code Online (Sandbox Code Playgroud)

两种情况之一的先决条件是什么意思?

if-then-throw语句以这种形式出现时,工具会将它们识别为遗留需要的语句。如果没有其他合约遵循 if-then-throw 序列,请使用Contract.EndContractBlock方法结束代码。


您也可以在Postconditions 中使用它:

什么是后置条件

后置条件是方法终止时的状态契约。在退出方法之前检查后置条件。失败后置条件的运行时行为由运行时分析器确定。

与前置条件不同,后置条件可能会引用可见性较低的成员。客户端可能无法理解或使用使用私有状态的后置条件表达的某些信息,但这并不影响客户端正确使用该方法的能力。

意思是,简而言之,后置条件帮助我们测试我们的方法。

一个例子是:

Contract.Ensures( this.F > 0 );
Run Code Online (Sandbox Code Playgroud)

请注意特殊的postcontions:

  • 您可以使用表达式在后置条件中引用方法返回值Contract.Result<T>(),其中T由方法的返回类型替换。当编译器无法推断类型时,您必须显式提供它。
  • 后置条件中的前状态值是指在方法或属性开始处的表达式的值。它使用表达式Contract.OldValue<T>(e),其中T是 的类型e。只要编译器能够推断其类型,就可以省略泛型类型参数。(例如,C# 编译器总是推断类型,因为它接受一个参数。)对于 e 中可能出现的内容以及旧表达式可能出现的上下文有几个限制。旧表达式不能包含另一个旧表达式。最重要的是,旧表达式必须引用存在于方法前提条件中的值。换句话说,它必须是一个表达式,只要方法的前提条件为真,就可以对其进行求值。

最后你有不变量:

对象不变量是当该对象对客户端可见时对于类的每个实例都应该为真的条件。它们表达了对象被认为是正确的条件。

意思是,不变量有助于测试我们的类代码和实例。

一个例子是:

[ContractInvariantMethod]  
protected void ObjectInvariant ()   
{  
Contract.Invariant(this.y >= 0);  
Contract.Invariant(this.x > this.y);  
...  
}  
Run Code Online (Sandbox Code Playgroud)

正确使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是对代码契约及其用法的一个很好的解释,但它没有回答 OP 的问题。@Kousic 的回答更好。 (7认同)
  • 我否决了这个答案,因为虽然它在 C# 8.0 中编译(在删除“FormUrlEncodedContent”的“内部”位之后),但代码的实际代码契约部分对构建过程没有影响(即将“null”传递给“ GetContentByteArray` 不会导致构建时错误或警告(当禁用 C# 8.0 可空引用类型时)。 (2认同)