下面是一个非常简单的例子。当我打开静态分析警告时,我仍然可以得到 警告CodeContracts:确保未经证实:Contract.Result()!= string.Empty
就行了
返回string.Format(“{0},{1}”, movie.Title,movie.Description);
请参阅我的以下代码
namespace CodeContractsSamples
{
public class MovieRepo
{
public string GetMovieInfo(Movie movie)
{
Contract.Requires(movie != null);
Contract.Ensures(Contract.Result<string>() != string.Empty);
return string.Format("{0}, {1}", movie.Title, movie.Description);
}
}
public class Movie
{
public string Title { get; set; }
public string Description { get; set; }
}
}
有什么想法吗?
答案 0 :(得分:8)
这是mscorlib dll中Contracts实现的限制。
请参阅官方代码合同论坛上的this link。
这是因为合同 string.Format不保证它的 结果是非空的,只有它是 非空。
修改支持此功能的一些证据: 在mscorlib.Contracts.dll上使用Reflector时,可以看到在String.Format上定义的合约
[Pure, Reads(ReadsAttribute.Reads.Nothing)]
public static string Format(string format, object[] args)
{
string str;
Contract.Requires((bool) (format != null), null, "format != null");
Contract.Requires((bool) (args != null), null, "args != null");
Contract.Ensures((bool) (Contract.Result<string>() != null), null, "Contract.Result<String>() != null");
return str;
}
如您所见,Contract.Result语句只是非空的,不是非空的。
答案 1 :(得分:1)
代码合同正确报告此问题。它无法证明返回的字符串不会为空。
真正的问题不在于Code Contracts的静态分析器。这是因为您没有提供足够的合同来确保此方法不会返回空字符串。
但是,您可以修复此方法,以便您可以向自己证明此方法不会返回空字符串,同时还可以帮助静态分析器。
注意强>
下面的代码中有很多注释 - 所以它看起来比现在更长。但是,希望你能更好地理解代码合约能做什么和不能做什么。此外,@ kenmetsu对
String.Format(string, params object args)
的代码合同也是正确的。然而,尽管如此,我在OP的代码中看到了一些问题,我觉得他们应该保证这个答案。这在以下代码的评论中阐明。
namespace CodeContractsSamples
{
public class MovieRepo
{
[Pure]
public string GetMovieInfo(Movie movie)
{
Contract.Requires(movie != null &&
!(string.IsNullOrEmpty(movie.Title) ||
string.IsNullOrEmpty(movie.Description)));
// This ensures was always invalid. Due to your format
// string, this string was guaranteed to never be empty--
// it would always contain at least ", ".
//Contract.Ensures(Contract.Result<string>() != string.Empty);
Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>()));
// Changed this line to use C# 6 -- see the below
//return string.Format("{0}, {1}", movie.Title, movie.Description);
// Help out the static analyzer--and yourself! First, we prove that
// Title and Description are greater than 0.
// These asserts aren't strictly needed, as this should
// follow from the pre-conditions above. But, I put this
// here just so you could follow the inference chain.
Contract.Assert(movie.Title.Length > 0);
Contract.Assert(movie.Description.Length > 0);
// Introduce an intermediate variable to help the static analyzer
var movieInfo = $"{movie.Title}, {movie.Description}";
// The static analyzer doesn't understand String.Format(...)
// (or string interpolation). However, above, we asserted that the movie
// Title's and Description's length are both greater than zero. Since we
// have proved that, then we can also prove that movieInfo's length is
// at least Title.Length + Description.Length + 2 (for the ", ");
// therefore, we know it's not null or empty.
// Again, the static analyzer will never be able to figure this out on
// it's own, so we tell it to assume that that's true. But, again, you
// have proven to yourself that this is indeed true; so we can safely tell
// the analyzer to assume this.
// If this seems like a lot of bloat--this is the cost of provable
// code--but you can rest assured that this method will always work the
// way you intended if the stated pre-conditions hold true upon method
// entry.
Contract.Assume(!string.IsNullOrEmpty(movieInfo));
return movieInfo;
}
}
// Since this class contained only data, and no behavior,
// I've changed this class to be an immutable value object.
// Also, since the class is immutable, it's also pure (i.e.
// no side-effects, so I've marked it as [Pure].
[Pure]
public class Movie : IEquatable<Movie>
{
private readonly string _title;
private readonly string _description;
[ContractInvariantMethod]
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "Required for code contracts.")]
private void ObjectInvariant()
{
Contract.Invariant(!(string.IsNullOrWhiteSpace(_title)
&& string.IsNullOrWhiteSpace(_description)));
}
public Movie(string title, string description)
{
// NOTE: For Code Contracts 1.9.10714.2, you will need to
// modify the Microsoft.CodeContract.targets file located
// at C:\Program Files (x86)\Microsoft\Contracts\MSBuild\v14.0
// (for VS 2015) in order for Code Contracts to be happy
// with the call to System.String.IsNullOrWhiteSpace(string)
// See this GitHub issue:
// https://github.com/Microsoft/CodeContracts/pull/359/files
Contract.Requires(!(string.IsNullOrWhiteSpace(title) &&
string.IsNullOrWhiteSpace(description)));
Contract.Ensures(_title == title &&
_description == description);
_title = title;
_description = description;
}
public string Title => _title;
public string Description => _description;
// Since this class is now an immutable value object, it's not
// a bad idea to override Equals and GetHashCode() and implement
// IEquatable<T>
public override bool Equals(object obj)
{
if (obj == null || !this.GetType().Equals(obj.GetType()))
{
return false;
}
Movie that = obj as Movie;
return this.Equals(that);
}
public override int GetHashCode()
{
// Because we know _title and _description will
// never be null due to class invariants, tell
// Code Contracts to assume this fact.
Contract.Assume(_title != null && _description != null);
return _title.GetHashCode() ^ _description.GetHashCode();
}
public bool Equals(Movie other)
{
if (other == null)
{
return false;
}
return this._title == other._title &&
this._description == other._description;
}
public static bool operator == (Movie movie1, Movie movie2)
{
if (((object)movie1) == null || ((object)movie2) == null)
{
return object.Equals(movie1, movie2);
}
return movie1.Equals(movie2);
}
public static bool operator != (Movie movie1, Movie movie2)
{
if (((object)movie1) == null || ((object)movie2) == null)
{
return !object.Equals(movie1, movie2);
}
return !movie1.Equals(movie2);
}
}
}
一旦我通过Code Contracts静态分析器运行上面的代码,我就没有警告。