Как бороться с предупреждением Code Contracts CC1036 при использовании string.IsNullOrWhiteSpace?
У меня есть следующий код:
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]
отсутствует?
Ответы
Ответ 1
Прохождение чистого делегата заставит предупреждение уйти. 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
Здесь мы имеем две точки:
1. Почему атрибут [Pure]
отсутствует в строковом классе для функции IsNullorWhiteSpace
?
2. Как устранить проблему с предупреждением CC1030?
Я попытаюсь обсудить оба.
1. Почему атрибут [Pure]
отсутствует?. Его не хватает, метаданные, похоже, не показывают этого.
Это не может быть помечено как Pure
в предыдущей версии .NET FX, поскольку они говорили:
Да, нам нужно сделать нашу проверку чувствительной к отключенной прагме...
Вздох.
В настоящее время у нас нет этого, но я добавил его к нашей рабочий список.
Обратитесь к 5-летнему обсуждению здесь.
Но это было отмечено как Pure
в последнем FX (4.6.1), см. .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
и вызовите это в условиях контракта.
Ответ 3
Как бы уродливо, вы можете обернуть функцию string.IsNullOrWhiteSpace
с помощью метода расширения и пометить эту новую функцию как Pure
.
Ответ 4
Я столкнулся с одной и той же проблемой. Я использую VS2015, поэтому он, похоже, не связан с версией VS. Я также тестировал один и тот же код на .NET 4.0, 4.5.1 и 4.6, не получая предупреждения.
Как и другие комментировали передо мной, IsNullOrWhiteSpace
помечен как [Pure]
в .NET 4.6.1, а также должен по умолчанию считаются чистыми по кодовым контрактам, поскольку они находятся в пространстве имен System.String
. Это делает его похожим на ошибку, поэтому я представил проблему с кодовыми контрактами об этом, поэтому с некоторой удачей мы скоро увидим официальный ответ.
Пока мы ждем ответа, возможно (например, @Jaco предлагает) обернуть его в метод расширения и пометить его как Pure
самостоятельно. При желании вы можете подавить предупреждение для этого конкретного метода следующим образом:
[SuppressMessage("Microsoft.Contracts", "CC1036", Justification = "string.IsNullOrWhiteSpace is Pure")]
... но обратите внимание, что это также подавляет это предупреждение из других определений Контракта тем же методом.
Ответ 5
Собственно, это проблема с тем, как компилируется .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
В обоих файлах дочерний элемент <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 pull, указанному выше), я больше не получал предупреждения статического анализа Code Contracts для использования String.IsNullOrWhiteSpace
.
Следует отметить, что ссылка на запрос на тягу была объединена в основной код для кодовых контрактов на GitHub; они просто еще не выпустили новый релиз, содержащий эти изменения.
Кроме того, для тех, кто заинтересован в смене "системных файлов", не стоит. Когда будет выпущена следующая версия Code Contracts, она установит обновленные версии этих файлов - и, надеюсь, изменения будут включены, и все будет правильно с миром. (Если, конечно, изменения не включены - в этом случае вы вернетесь сюда, чтобы ссылаться на это сообщение, чтобы снова внести эти изменения;) lol.)