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

时间:2017-03-09 22:24:24

标签: c# .net-core code-contracts

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

消息说明:

  

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

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

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

1 个答案:

答案 0 :(得分:2)

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

  

代码合同提供了一种方法来指定前提条件,后置条件,   和代码中的对象不变式。前提条件是要求   输入方法或属性时必须满足的条件。后置条件   在方法或属性代码退出时描述期望值。   对象不变量描述了处于   状态很好。

意思是,为了简单起见,CodeContracts帮助我们简化了代码中的测试。

我们如何使用代码合同?

考虑以下示例:

if ( x == null ) throw new ...  
Contract.EndContractBlock(); // All previous "if" checks are preconditions  

两个条件之一的前提条件是什么意思?

  • 该语句出现在方法中的任何其他语句之前。
  • 此类语句的整个集合之后是显式的Contract方法调用,例如对RequiresEnsuresEnsuresOnThrow或{{3 }}方法。

if-then-throw语句以这种形式出现时,工具会将其识别为旧版require语句。如果没有其他合同遵循if-then-throw序列,则以EndContractBlock方法结束代码。


您也可以在后置条件中使用它:

什么是后置条件

  

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

     

与先决条件不同,后置条件可以引用较少的成员   能见度。客户可能无法理解或利用某些   后置条件使用私有状态表示的信息中,   但这不会影响客户使用该方法的能力   正确。

意思是,为了简短起见,后置条件可以帮助我们测试方法。

一个例子是:

Contract.Ensures( this.F > 0 );

请注意特殊的后置条件:

  • 您可以使用表达式Contract.Result<T>()在后​​置条件中引用方法返回值,其中T被方法的返回类型代替。当编译器无法推断类型时,您必须显式提供它。
  • 后置条件中的状态前值是指方法或属性开始处的表达式值。它使用表达式Contract.OldValue<T>(e),其中Te的类型。只要编译器能够推断其类型,就可以省略泛型类型参数。 (例如,C#编译器始终会推断类型,因为它需要一个参数。)对于e中可能发生的情况以及可能会出现旧表达式的上下文有一些限制。一个旧表达式不能包含另一个旧表达式。最重要的是,旧表达式必须引用方法的前提条件状态中存在的值。换句话说,只要该方法的前提为true,它就必须是可以计算的表达式。

最后,您具有不变式:

  

对象不变式是每个实例都应为真的条件   只要对象对客户可见,该类的名称。他们表达   认为该物体正确的条件。

含义,不变式有助于测试我们的类代码和实例。

一个例子是:

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