如何处理使用string.IsNullOrWhiteSpace时的Code Contracts警告CC1036?

28

我有以下的代码合约:

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

    throw new NotImplementedException();
}

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

  

警告 CC1036:在方法 '[...]' 的合同中检测到对方法 'System.String.IsNullOrWhiteSpace(System.String)' 的调用,但没有标记为[Pure]

如何处理?

奇怪的是,我还在其他合同中使用了string.IsNullOrEmpty,这也没有被标记为[Pure],但重写器并没有遇到任何问题。

我的合同重写器版本是1.9.10714.2。

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

#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] 特性?


@Sinatr 很有趣 - 如标签中所述,我的“目标框架”设置为4.6.1。重写器可能在不同的上下文中起作用吗? - BartoszKP
看起来像是一个错误 - Sinatr
该方法在.NET Framework源代码中被标记为纯净的,适用于.NET 4.6.1:http://referencesource.microsoft.com/#mscorlib/system/string.cs,8281103e6f23cb5c - Alex
@Jaco 如果您认为我的问题有误或缺少重要信息,我很乐意进行改进。 - BartoszKP
3
在 System.String 方法中,[Pure] 标记甚至都不应该是必要的。根据文档(第 5.4 节),工具会自动假定以 "System.Diagnostics.Contracts.Contract"、"System.String"、"System.IO.Path" 或 "System.Type" 开头的完全限定名称的任何方法都是纯的。 - Mark Waterman
显示剩余5条评论
5个回答

8

这里有两个问题:

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

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

我将尝试讨论这两个问题。

1. 为什么缺少 [Pure] 属性? 它并没有缺失,元数据似乎没有显示出来。

在之前的 .NET FX 版本中,这可能没有被标记为 Pure,因为他们说:

是的,我们需要使我们的检查器对禁用的 pragma 敏感...

叹气。

我们目前还没有实现它,但我已经将其添加到我们的工作列表中。

请参考5年前的讨论这里

但是在最新的FX(4.6.1)中,这被标记为Pure,请参考.NET Framework 4.6.1,新的string类代码

[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,开发人员昨天才提出了这个问题(请参考此处)。

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

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

相同的问题也适用于 Invariant()。给定以下代码,会显示相同的警告:
private string testString = "test";

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

如何解决?

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


你使用的是哪个确切版本的.NET 4.6.1?例如,对于.NET 4.5.1,有多个版本,版本范围从4.0.30319.18401到4.0.30319.34000,请参见https://msdn.microsoft.com/en-us/library/hh925568(v=vs.110).aspx。 - Alex

7

通过使用纯代理,可以消除警告。 Predicate<T>已标记为纯,因此您可以使用它来解决这个问题:

// 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();
}

最终我创建了一个帮助类public static class CodeContractsBugFix,其中包含了你提出的谓词。谢谢! - BartoszKP
有人知道为什么这个代码在1.9.10714.2版本中不再起作用了吗?是这个答案中显示的版本还是@BartoszKP助手类?我都试过了,但分析器仍然报错。 - fourpastmidnight
这对我不再起作用了。然而,我在我的答案中注意到了一种告诉Code Contracts使用哪些.NET程序集来解决v4.6+ .NET Framework的问题的方法,从而解决了OP的问题。 - fourpastmidnight

5

虽然有点丑陋,但是你可以使用扩展方法包装函数 string.IsNullOrWhiteSpace 并将这个新函数标记为 Pure


3

实际上,这是.NET 4.6+编译方式的一个问题。请参见此GitHub拉取请求

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

  • 对于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 pull request 对这些文件进行更改后,我不再收到有关使用 String.IsNullOrWhiteSpace 的 Code Contracts 静态分析警告。
需要注意的是,参考的 pull request 已经合并到了 Code Contracts 的主代码中,只是他们还没有发布包含这些更改的新版本。
此外,对于那些担心更改“系统文件”的人,不用担心。当 Code Contracts 的下一个版本发布时,它将安装这些文件的更新版本 - 希望这些更改也会被包含在内,一切都会变得正确。 (当然,如果更改没有被包含在内的话,你将会回到这里参考这篇文章再次进行更改;) lol.)

3

我遇到了完全相同的问题。我正在使用VS2015,因此似乎与VS版本无关。我还在.NET 4.0、4.5.1和4.6上测试了完全相同的代码,但没有收到警告。

正如其他人在我之前评论的那样,《IsNullOrWhiteSpace在.NET 4.6.1中被标记为[Pure]》,并且默认情况下应该被Code Contracts视为纯净,因为它位于System.String命名空间中。这使得它看起来像是一个bug,因此我已经提交了有关此问题的代码合同问题,所以希望我们很快能看到官方答案。

在等待答案的同时,可以像@Jaco建议的那样将其包装在扩展方法中,并将其标记为。可以选择像这样抑制该特定方法的警告:

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

请注意,这也将抑制同一方法中其他合约定义中的警告。


看起来这只是一个问题,与Code Contracts使用错误的.NET 4.6.1参考程序集有关。 - Desarc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接