使用string.IsNullOrWhiteSpace时如何处理代码合同警告CC1036?

How to deal with Code Contracts warning CC1036 when using string.IsNullOrWhiteSpace?

我有以下代码合约:

public void F(string x)
{
    Contract.Requires(!string.IsNullOrWhiteSpace(x));

    throw new NotImplementedException();
}

编译时,我收到以下警告:

warning CC1036: Detected call to method 'System.String.IsNullOrWhiteSpace(System.String)' without [Pure] in contracts of method [...]

如何处理?

奇怪的是,我也在使用 string.IsNullOrEmpty,它在其他合同中也没有标记为 [Pure],并且重写器对此没有问题。

我的 Contract Rewriter 的版本是 1.9.10714.2。

这是我正在使用的 String class 实现的相关部分(从元数据中检索):

#region Assembly mscorlib.dll, v4.0.0.0
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\mscorlib.dll
#endregion

using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;

namespace System
{
    // Summary:
    //     Represents text as a series of Unicode characters.To browse the .NET Framework
    //     source code for this type, see the Reference Source.
    [Serializable]
    [ComVisible(true)]
    public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable, IComparable<string>, IEnumerable<char>, IEquatable<string>
    {

    // [...]

        //
        // Summary:
        // [...]
        public static bool IsNullOrEmpty(string value);
        //
        // Summary:
        // [...]
        public static bool IsNullOrWhiteSpace(string value);

为什么缺少 [Pure] 属性?

虽然有点难看,但您可以用扩展方法包装函数 string.IsNullOrWhiteSpace,并将这个新函数标记为 Pure

我刚遇到完全相同的问题。我用的是VS2015,所以好像和VS版本没有关系。我还在 .NET 4.0、4.5.1 和 4.6 上测试了完全相同的代码,但没有收到警告。

就像其他人在我之前发表的评论一样,the IsNullOrWhiteSpace is marked as [Pure] in .NET 4.6.1, and additionally should by default be considered pure by Code Contracts because it is in the System.String namespace. This makes it look like a bug, so I have submitted an issue to Code Contracts about this,运气好的话我们很快就会看到正式的答复。

在我们等待答案的同时,可以(如@Jaco 建议的那样)将其包装在扩展方法中并自己标记为 Pure。或者,您可以像这样抑制该特定方法的警告:

[SuppressMessage("Microsoft.Contracts", "CC1036", Justification = "string.IsNullOrWhiteSpace is Pure")]

...但请注意,这也会以相同的方法抑制来自其他合同定义的警告。

这里有两点:

1.为什么 IsNullorWhiteSpace 函数的字符串 class 中缺少 [Pure] 属性?

2。如何解决 CC1030 警告问题?

我会尝试讨论两者。

1. Why is the [Pure] attribute missing? It's not missing, metadata does not seem to be showing this.

这在以前版本的 .NET FX 中可能未标记为 Pure,因为他们说:

Yes, we need to make our checker sensitive to the disable pragma...

唉。

We currently don't have that implemented, but I've added it to our work list.

参考5年前的讨论here

但这在最新的FX(4.6.1)中已经被标记为Pure,参考.NET Framework 4.6.1,新的string class code.

[Pure]
public static bool IsNullOrWhiteSpace(String value) {
    if (value == null) return true;

    for(int i = 0; i < value.Length; i++) {
        if(!Char.IsWhiteSpace(value[i])) return false;
    }

    return true;
}

那为什么是CC1036?

这个警告"CC1036"来自CodeContracts,开发者昨天才打开这个问题(refer here)。

现在为什么元数据没有吐出 Pure 属性,这是一个不同的问题,比如 Equals 方法,添加了 Pure 但只显示 SecuritySafeCritical在元数据代码中。

[SecuritySafeCritical]
public static bool Equals(String a, String b, StringComparison comparisonType);

The same problem applies to Invariant(). Given the following code, the same warnings are displayed:

private string testString = "test";

[ContractInvariantMethod]
private void TestInvariant()
{
     Contract.Invariant(!string.IsNullOrWhiteSpace(testString));
}

如何解决?

正如其他人也建议的那样,创建另一个方法,将其标记为 Pure 并在您的合同条件中调用它。

通过纯委托将使警告消失。 Predicate<T> 已经被标记为 pure,所以你可以用它来解决这个错误:

// Workaround for https://github.com/Microsoft/CodeContracts/issues/339
public Predicate<string> IsNullOrWhiteSpace = string.IsNullOrWhiteSpace;

public void F(string x)
{
    Contract.Requires(!IsNullOrWhiteSpace(x));

    throw new NotImplementedException();
}

其实这是.NET 4.6+编译方式的问题。看到这个 GitHub pull request.

我能够通过修改以下文件解决此问题:

  • 对于 Visual Studio 2013 年:
    • C:\Program Files (x86)\Microsoft\Contracts\MsBuild\v12.0\Microsoft.CodeContracts.Targets
  • 对于 Visual Studio 2015 年:
    • C:\Progarm Files (x86)\Microsoft\Contracts\MsBuild\v14.0\Microsoft.CodeContracts.Targets

在这两个文件中,确保第一个 <Choose> 元素的 <Otherwise> 子元素具有如下所示的内容:

...
<Choose>
  <When Condition="'$(TargetFrameworkIdentifier)' == 'Silverlight'">
     ...
  </When>
  <Otherwise>
    <Choose>
      <When Condition="'$(TargetFrameworkVersion)' == 'v4.0">
        <PropertyGroup>
          <CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.0</CodeContractsReferenceAssemblyLibPath>
        </PropertyGroup>
      </When>
      <When Condition="'$(TargetFrameworkVersion)' == 'v4.5'">
        <PropertyGroup>
          <CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.5</CodeContractsReferenceAssemblyLibPath>
        </PropertyGroup>
      </When>
      <When Condition="'$(TargetFrameworkVersion)' == 'v4.5.1'">
        <PropertyGroup>
          <CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.5</CodeContractsReferenceAssemblyLibPath>
        </PropertyGroup>
      </When>
      <When Condition="'$(TargetFrameworkVersion)' == 'v4.5.2'">
        <PropertyGroup>
          <CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.5</CodeContractsReferenceAssemblyLibPath>
        </PropertyGroup>
      </When>
      <When Condition="'$(TargetFrameworkVersion)' == 'v4.6'">
        <PropertyGroup>
          <CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.5</CodeContractsReferenceAssemblyLibPath>
        </PropertyGroup>
      </When>
      <When Condition="'$(TargetFrameworkVersion)' == 'v4.6.1'">
        <PropertyGroup>
          <CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.5</CodeContractsReferenceAssemblyLibPath>
        </PropertyGroup>
      </When>
      <Otherwise>
        <PropertyGroup>
          <CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v3.5</CodeContractsReferenceAssemblyLibPath>
        </PropertyGroup>
      </Otherwise>
    </Choose>
  </Otherwise>
</Chose>
...

对这些文件进行这些更改后(根据上面引用的 GitHub 拉取请求),我不再收到使用 String.IsNullOrWhiteSpace.

的代码契约静态分析警告

请注意,引用的拉取请求已合并到 GitHub 上的代码合同的主要代码中;他们只是还没有发布包含这些更改的新版本。

此外,对于那些担心更改 "system files" 的人,请不要担心。当发布下一个版本的 Code Contracts 时,它将安装这些文件的更新版本——希望这些更改将被包含在内,并且世界上的一切都将是正确的。 (当然,除非不包括更改——在这种情况下,您将返回此处参考此 post 以再次进行这些更改;)哈哈。)