我有以下代码合同:
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]
属性?
答案 0 :(得分:8)
这里有两点:
<强> 1。为什么[Pure]
函数的字符串类中缺少IsNullorWhiteSpace
属性?
<强> 2。如何解决CC1030警告问题?
我会尝试讨论两者。
<强> 1。为什么缺少
[Pure]
属性?它没有丢失,元数据似乎没有显示出来。
在以前版本的.NET FX中,这可能不会被标记为Pure
,因为他们说:
是的,我们需要让我们的检查器对禁用pragma敏感 ......
叹息。
我们目前没有实施,但我已将其添加到我们的 工作清单。
请参阅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);
同样的问题适用于
Invariant()
。鉴于以下代码, 显示相同的警告:
private string testString = "test";
[ContractInvariantMethod]
private void TestInvariant()
{
Contract.Invariant(!string.IsNullOrWhiteSpace(testString));
}
如何解决?
正如其他人也建议的那样,创建另一种方法,将其标记为Pure
并在合同条件下调用此方法。
答案 1 :(得分: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();
}
答案 2 :(得分:5)
虽然有点难看,但您可以使用扩展方法包装函数string.IsNullOrWhiteSpace
,并将此新函数标记为Pure
。
答案 3 :(得分:3)
我刚刚遇到了完全相同的问题。我正在使用VS2015,所以它似乎与VS版本无关。我还在.NET 4.0,4.5.1和4.6上测试了完全相同的代码,但未收到警告。
正如其他人在我之前评论的the IsNullOrWhiteSpace
is marked as [Pure]
in .NET 4.6.1,并且默认情况下应该被代码契约视为纯粹的,因为它位于System.String
命名空间中。这使它看起来像一个bug,所以我提交了an issue to Code Contracts about this,所以运气好的话我们很快就能看到正式答案。
在我们等待答案的同时,有可能(如@Jaco建议的那样)将其包装在扩展方法中并自己标记为Pure
。 (可选)您可以取消对该特定方法的警告,如下所示:
[SuppressMessage("Microsoft.Contracts", "CC1036", Justification = "string.IsNullOrWhiteSpace is Pure")]
...但请注意,这也会在同一方法中禁止来自其他合约定义的此警告。
答案 4 :(得分:3)
实际上,这是.NET 4.6+编译方式的问题。请参阅此GitHub pull request。
我能够通过修改以下文件来解决这个问题:
C:\Program Files (x86)\Microsoft\Contracts\MsBuild\v12.0\Microsoft.CodeContracts.Targets
C:\Progarm Files (x86)\Microsoft\Contracts\MsBuild\v14.0\Microsoft.CodeContracts.Targets
在这两个文件中,确保第一个<Otherwise>
元素的<Choose>
子元素具有以下内容:
...
<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
的代码合同静态分析警告。
应该注意的是,引用的pull请求已合并到GitHub上的Code Contracts的主代码中;他们还没有发布包含这些变化的新版本。
此外,对于那些担心更改系统文件的人来说,不是。当下一版本的Code Contracts发布时,它将安装这些文件的更新版本 - 并且希望包含这些更改,并且所有这些都将适用于全世界。 (当然,除非包含更改 - 在这种情况下,您将回到此处参考此帖子以再次进行更改;)lol。)