为什么 Code Contracts 声称此代码 "Ensures is false"?
Why does Code Contracts claim that "Ensures is false" for this code?
这是我的 类:
之一的构造函数
public SemanticVersion(string version)
{
Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(version));
Contract.Ensures(MajorVersion >= 0);
Contract.Ensures(MinorVersion >= 0);
Contract.Ensures(PatchVersion >= 0);
Contract.Ensures(PrereleaseVersion != null);
Contract.Ensures(BuildVersion != null);
var match = SemanticVersionRegex.Match(version);
if (!match.Success)
{
var message = $"The version number '{version}' is not a valid semantic version number.";
throw new ArgumentException(message, nameof(version));
}
MajorVersion = int.Parse(match.Groups["major"].Value, CultureInfo.InvariantCulture);
MinorVersion = int.Parse(match.Groups["minor"].Value, CultureInfo.InvariantCulture);
PatchVersion = int.Parse(match.Groups["patch"].Value, CultureInfo.InvariantCulture);
PrereleaseVersion = match.Groups["prerelease"].Success
? new Maybe<string>(match.Groups["prerelease"].Value)
: Maybe<string>.Empty;
BuildVersion = match.Groups["build"].Success
? new Maybe<string>(match.Groups["build"].Value)
: Maybe<string>.Empty;
}
代码合同静态检查器标记错误:
warning : CodeContracts: ensures is false: PrereleaseVersion != null
Maybe<T>
是一个包含零个或一个元素的集合。
据我所知,唯一可以为 null 的方法是在分配之前是否存在异常,这应该使 Ensures 要求无关紧要。我会代码盲吗?你能看出问题所在吗...?
更新:发布 Maybe 的实现以回应评论。
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
namespace TA.CoreTypes
{
/// <summary>
/// Represents an object that may or may not have a value (strictly, a collection of zero or one elements). Use
/// LINQ expression
/// <c>maybe.Any()</c> to determine if there is a value. Use LINQ expression
/// <c>maybe.Single()</c> to retrieve the value.
/// </summary>
/// <typeparam name="T">The type of the item in the collection.</typeparam>
public class Maybe<T> : IEnumerable<T>
{
private static readonly Maybe<T> EmptyInstance = new Maybe<T>();
private readonly IEnumerable<T> values;
/// <summary>
/// Initializes a new instance of the <see cref="Maybe{T}" /> with no value.
/// </summary>
private Maybe()
{
values = new T[0];
}
/// <summary>
/// Initializes a new instance of the <see cref="Maybe{T}" /> with a value.
/// </summary>
/// <param name="value">The value.</param>
public Maybe(T value)
{
Contract.Requires(value != null);
values = new[] {value};
}
/// <summary>
/// Gets an instance that does not contain a value.
/// </summary>
/// <value>The empty instance.</value>
public static Maybe<T> Empty
{
get
{
Contract.Ensures(Contract.Result<Maybe<T>>() != null);
return EmptyInstance;
}
}
public IEnumerator<T> GetEnumerator()
{
Contract.Ensures(Contract.Result<IEnumerator<T>>() != null);
return values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
Contract.Ensures(Contract.Result<IEnumerator>() != null);
return GetEnumerator();
}
[ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(values != null);
}
[Pure]
public override string ToString()
{
Contract.Ensures(Contract.Result<string>() != null);
if (Equals(Empty)) return "{no value}";
return this.Single().ToString();
}
}
public static class MaybeExtensions
{
public static bool None<T>(this Maybe<T> maybe)
{
if (maybe == null) return true;
if (maybe == Maybe<T>.Empty) return true;
return !maybe.Any();
}
}
}
如果 match.Success
为假,则可能是 null
。
试试这个...
public SemanticVersion(string version)
{
Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(version));
Contract.Ensures(MajorVersion >= 0);
Contract.Ensures(MinorVersion >= 0);
Contract.Ensures(PatchVersion >= 0);
Contract.Ensures(PrereleaseVersion != null);
Contract.Ensures(BuildVersion != null);
var match = SemanticVersionRegex.Match(version);
if (!match.Success)
{
// set the values here
PrereleaseVersion = Maybe<string>.Empty;
BuildVersion = Maybe<string>.Empty;
var message = $"The version number '{version}' is not a valid semantic version number.";
throw new ArgumentException(message, nameof(version));
}
MajorVersion = int.Parse(match.Groups["major"].Value, CultureInfo.InvariantCulture);
MinorVersion = int.Parse(match.Groups["minor"].Value, CultureInfo.InvariantCulture);
PatchVersion = int.Parse(match.Groups["patch"].Value, CultureInfo.InvariantCulture);
PrereleaseVersion = match.Groups["prerelease"].Success
? new Maybe<string>(match.Groups["prerelease"].Value)
: Maybe<string>.Empty;
BuildVersion = match.Groups["build"].Success
? new Maybe<string>(match.Groups["build"].Value)
: Maybe<string>.Empty;
}
...根据此处的评论,这是一个异常期间状态发生变化的示例。
class Program
{
static void Main(string[] args)
{
var obj = new MyObject() { Prop1 = "Hello", };
Console.WriteLine(obj.Prop1);
try
{
obj.DoWork();
}
catch (Exception)
{
}
Console.WriteLine(obj.Prop1);
/*
Hello
World!
*/
}
}
public class MyObject
{
public string Prop1 { get; set; }
public void DoWork()
{
this.Prop1 = "World!";
throw new Exception();
}
}
Maybe<T>
class 上唯一的增变器方法似乎是构造函数本身。其他所有方法都只是得到东西。因此,您可以将 PureAttribute
放在 class 级别以向分析器提示整个 class 是纯的,因为它是不可变的——毕竟,这不是 Maybe 的重点吗,要么你得到一些东西,要么你得到一个 Empty Maybe,但你永远不会得到 null?而且你不能改变一个 Maybe 中的值,你只能创建一个包含新值的新 Maybe。
此外,我一直在使用静态分析器和使用 Contract.Ensures
构造函数中的属性(特别是可变属性)时遇到问题,即使指定了对象不变量也是如此;我不太清楚这是为什么。
无论如何,如果您有不可变属性,那么下面的代码应该可以工作:
// If this class is immutable, consider marking it with:
// [Pure]
public class SemanticVersion
{
private readonly int _majorVersion;
private readonly int _minorVersion;
private readonly int _patchVersion;
private readonly Maybe<T> _buildVersion;
private readonly Maybe<T> _prereleaseVersion;
public SemanticVersion(string version)
{
Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(version));
var match = SemanticVersionRegex.Match(version);
if (!match.Success)
{
var message = $"The version number '{version}' is not a valid semantic version number.";
throw new ArgumentException(message, nameof(version));
}
_majorVersion = int.Parse(match.Groups["major"].Value, CultureInfo.InvariantCulture);
_minorVersion = int.Parse(match.Groups["minor"].Value, CultureInfo.InvariantCulture);
_patchVersion = int.Parse(match.Groups["patch"].Value, CultureInfo.InvariantCulture);
_prereleaseVersion = match.Groups["prerelease"].Success
? new Maybe<string>(match.Groups["prerelease"].Value)
: Maybe<string>.Empty;
_buildVersion = match.Groups["build"].Success
? new Maybe<string>(match.Groups["build"].Value)
: Maybe<string>.Empty;
}
[ContractInvariantMethod]
private void ObjectInvariants()
{
Contract.Invariant(_majorVersion >= 0);
Contract.Invariant(_minorVersion >= 0);
Contract.Invariant(_patchVersion >= 0);
Contract.Invariant(_prereleaseVersion != null);
Contract.Invariant(_buildVersion != null);
}
// Properties that only contain getters are automatically
// considered Pure by Code Contracts. But also, this point
// is moot if you make the entire class Pure if it's
// immutable.
public int MajorVersion => _majorVersion;
public int MinorVersion => _minorVersion;
public int PatchVersion => _patchVersion;
public Maybe<T> PrereleaseVersion => _prereleaseVersion;
public Maybe<T> BuildVersion => _buildVersion;
}
如果您的 class 不纯并且属性是可变的,那么您将需要创建引用私有支持字段的完整属性。但是相同的 ObjectInvariants
方法应该足以用适当的合约来检测您的 class。
这是我的 类:
之一的构造函数public SemanticVersion(string version)
{
Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(version));
Contract.Ensures(MajorVersion >= 0);
Contract.Ensures(MinorVersion >= 0);
Contract.Ensures(PatchVersion >= 0);
Contract.Ensures(PrereleaseVersion != null);
Contract.Ensures(BuildVersion != null);
var match = SemanticVersionRegex.Match(version);
if (!match.Success)
{
var message = $"The version number '{version}' is not a valid semantic version number.";
throw new ArgumentException(message, nameof(version));
}
MajorVersion = int.Parse(match.Groups["major"].Value, CultureInfo.InvariantCulture);
MinorVersion = int.Parse(match.Groups["minor"].Value, CultureInfo.InvariantCulture);
PatchVersion = int.Parse(match.Groups["patch"].Value, CultureInfo.InvariantCulture);
PrereleaseVersion = match.Groups["prerelease"].Success
? new Maybe<string>(match.Groups["prerelease"].Value)
: Maybe<string>.Empty;
BuildVersion = match.Groups["build"].Success
? new Maybe<string>(match.Groups["build"].Value)
: Maybe<string>.Empty;
}
代码合同静态检查器标记错误:
warning : CodeContracts: ensures is false: PrereleaseVersion != null
Maybe<T>
是一个包含零个或一个元素的集合。
据我所知,唯一可以为 null 的方法是在分配之前是否存在异常,这应该使 Ensures 要求无关紧要。我会代码盲吗?你能看出问题所在吗...?
更新:发布 Maybe 的实现以回应评论。
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
namespace TA.CoreTypes
{
/// <summary>
/// Represents an object that may or may not have a value (strictly, a collection of zero or one elements). Use
/// LINQ expression
/// <c>maybe.Any()</c> to determine if there is a value. Use LINQ expression
/// <c>maybe.Single()</c> to retrieve the value.
/// </summary>
/// <typeparam name="T">The type of the item in the collection.</typeparam>
public class Maybe<T> : IEnumerable<T>
{
private static readonly Maybe<T> EmptyInstance = new Maybe<T>();
private readonly IEnumerable<T> values;
/// <summary>
/// Initializes a new instance of the <see cref="Maybe{T}" /> with no value.
/// </summary>
private Maybe()
{
values = new T[0];
}
/// <summary>
/// Initializes a new instance of the <see cref="Maybe{T}" /> with a value.
/// </summary>
/// <param name="value">The value.</param>
public Maybe(T value)
{
Contract.Requires(value != null);
values = new[] {value};
}
/// <summary>
/// Gets an instance that does not contain a value.
/// </summary>
/// <value>The empty instance.</value>
public static Maybe<T> Empty
{
get
{
Contract.Ensures(Contract.Result<Maybe<T>>() != null);
return EmptyInstance;
}
}
public IEnumerator<T> GetEnumerator()
{
Contract.Ensures(Contract.Result<IEnumerator<T>>() != null);
return values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
Contract.Ensures(Contract.Result<IEnumerator>() != null);
return GetEnumerator();
}
[ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(values != null);
}
[Pure]
public override string ToString()
{
Contract.Ensures(Contract.Result<string>() != null);
if (Equals(Empty)) return "{no value}";
return this.Single().ToString();
}
}
public static class MaybeExtensions
{
public static bool None<T>(this Maybe<T> maybe)
{
if (maybe == null) return true;
if (maybe == Maybe<T>.Empty) return true;
return !maybe.Any();
}
}
}
如果 match.Success
为假,则可能是 null
。
试试这个...
public SemanticVersion(string version)
{
Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(version));
Contract.Ensures(MajorVersion >= 0);
Contract.Ensures(MinorVersion >= 0);
Contract.Ensures(PatchVersion >= 0);
Contract.Ensures(PrereleaseVersion != null);
Contract.Ensures(BuildVersion != null);
var match = SemanticVersionRegex.Match(version);
if (!match.Success)
{
// set the values here
PrereleaseVersion = Maybe<string>.Empty;
BuildVersion = Maybe<string>.Empty;
var message = $"The version number '{version}' is not a valid semantic version number.";
throw new ArgumentException(message, nameof(version));
}
MajorVersion = int.Parse(match.Groups["major"].Value, CultureInfo.InvariantCulture);
MinorVersion = int.Parse(match.Groups["minor"].Value, CultureInfo.InvariantCulture);
PatchVersion = int.Parse(match.Groups["patch"].Value, CultureInfo.InvariantCulture);
PrereleaseVersion = match.Groups["prerelease"].Success
? new Maybe<string>(match.Groups["prerelease"].Value)
: Maybe<string>.Empty;
BuildVersion = match.Groups["build"].Success
? new Maybe<string>(match.Groups["build"].Value)
: Maybe<string>.Empty;
}
...根据此处的评论,这是一个异常期间状态发生变化的示例。
class Program
{
static void Main(string[] args)
{
var obj = new MyObject() { Prop1 = "Hello", };
Console.WriteLine(obj.Prop1);
try
{
obj.DoWork();
}
catch (Exception)
{
}
Console.WriteLine(obj.Prop1);
/*
Hello
World!
*/
}
}
public class MyObject
{
public string Prop1 { get; set; }
public void DoWork()
{
this.Prop1 = "World!";
throw new Exception();
}
}
Maybe<T>
class 上唯一的增变器方法似乎是构造函数本身。其他所有方法都只是得到东西。因此,您可以将 PureAttribute
放在 class 级别以向分析器提示整个 class 是纯的,因为它是不可变的——毕竟,这不是 Maybe 的重点吗,要么你得到一些东西,要么你得到一个 Empty Maybe,但你永远不会得到 null?而且你不能改变一个 Maybe 中的值,你只能创建一个包含新值的新 Maybe。
此外,我一直在使用静态分析器和使用 Contract.Ensures
构造函数中的属性(特别是可变属性)时遇到问题,即使指定了对象不变量也是如此;我不太清楚这是为什么。
无论如何,如果您有不可变属性,那么下面的代码应该可以工作:
// If this class is immutable, consider marking it with:
// [Pure]
public class SemanticVersion
{
private readonly int _majorVersion;
private readonly int _minorVersion;
private readonly int _patchVersion;
private readonly Maybe<T> _buildVersion;
private readonly Maybe<T> _prereleaseVersion;
public SemanticVersion(string version)
{
Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(version));
var match = SemanticVersionRegex.Match(version);
if (!match.Success)
{
var message = $"The version number '{version}' is not a valid semantic version number.";
throw new ArgumentException(message, nameof(version));
}
_majorVersion = int.Parse(match.Groups["major"].Value, CultureInfo.InvariantCulture);
_minorVersion = int.Parse(match.Groups["minor"].Value, CultureInfo.InvariantCulture);
_patchVersion = int.Parse(match.Groups["patch"].Value, CultureInfo.InvariantCulture);
_prereleaseVersion = match.Groups["prerelease"].Success
? new Maybe<string>(match.Groups["prerelease"].Value)
: Maybe<string>.Empty;
_buildVersion = match.Groups["build"].Success
? new Maybe<string>(match.Groups["build"].Value)
: Maybe<string>.Empty;
}
[ContractInvariantMethod]
private void ObjectInvariants()
{
Contract.Invariant(_majorVersion >= 0);
Contract.Invariant(_minorVersion >= 0);
Contract.Invariant(_patchVersion >= 0);
Contract.Invariant(_prereleaseVersion != null);
Contract.Invariant(_buildVersion != null);
}
// Properties that only contain getters are automatically
// considered Pure by Code Contracts. But also, this point
// is moot if you make the entire class Pure if it's
// immutable.
public int MajorVersion => _majorVersion;
public int MinorVersion => _minorVersion;
public int PatchVersion => _patchVersion;
public Maybe<T> PrereleaseVersion => _prereleaseVersion;
public Maybe<T> BuildVersion => _buildVersion;
}
如果您的 class 不纯并且属性是可变的,那么您将需要创建引用私有支持字段的完整属性。但是相同的 ObjectInvariants
方法应该足以用适当的合约来检测您的 class。