Я играл с Code Contracts, и мне действительно нравится то, что я видел до сих пор.Они побуждают меня оценивать и явно декларировать мои предположения, что уже помогло мне выявить несколько угловых случаев, которые я не рассматривал в коде, к которому я добавляю контракты.Прямо сейчас я играю с попыткой применить более сложные инварианты.У меня есть один случай, который в настоящее время не доказывает, и мне любопытно, есть ли способ исправить это, кроме простого добавления вызовов Contract.Assume.Вот класс, о котором идет речь, для удобства чтения:
public abstract class MemoryEncoder
{
private const int CapacityDelta = 16;
private int _currentByte;
/// <summary>
/// The current byte index in the encoding stream.
/// This should not need to be modified, under typical usage,
/// but can be used to randomly access the encoding region.
/// </summary>
public int CurrentByte
{
get
{
Contract.Ensures(Contract.Result<int>() >= 0);
Contract.Ensures(Contract.Result<int>() <= Length);
return _currentByte;
}
set
{
Contract.Requires(value >= 0);
Contract.Requires(value <= Length);
_currentByte = value;
}
}
/// <summary>
/// Current number of bytes encoded in the buffer.
/// This may be less than the size of the buffer (capacity).
/// </summary>
public int Length { get; private set; }
/// <summary>
/// The raw buffer encapsulated by the encoder.
/// </summary>
protected internal Byte[] Buffer { get; private set; }
/// <summary>
/// Reserve space in the encoder buffer for the specified number of new bytes
/// </summary>
/// <param name="bytesRequired">The number of bytes required</param>
protected void ReserveSpace(int bytesRequired)
{
Contract.Requires(bytesRequired > 0);
Contract.Ensures((Length - CurrentByte) >= bytesRequired);
//Check if these bytes would overflow the current buffer););
if ((CurrentByte + bytesRequired) > Buffer.Length)
{
//Create a new buffer with at least enough space for the additional bytes required
var newBuffer = new Byte[Buffer.Length + Math.Max(bytesRequired, CapacityDelta)];
//Copy the contents of the previous buffer and replace the original buffer reference
Buffer.CopyTo(newBuffer, 0);
Buffer = newBuffer;
}
//Check if the total length of written bytes has increased
if ((CurrentByte + bytesRequired) > Length)
{
Length = CurrentByte + bytesRequired;
}
}
[ContractInvariantMethod]
private void GlobalRules()
{
Contract.Invariant(Buffer != null);
Contract.Invariant(Length <= Buffer.Length);
Contract.Invariant(CurrentByte >= 0);
Contract.Invariant(CurrentByte <= Length);
}
}
Меня интересует, как я могу структурировать вызовы Contract в ReserveSpace, чтобы инварианты классов были доказуемы.В частности, он жалуется на (Length <= Buffer.Length) и (CurrentByte <= Length).Для меня разумно, что он не может видеть, что (Length <= Buffer.Length) удовлетворен, так как он создает новый буфер и переназначает ссылку.Является ли мой единственный вариант добавления предположить, что инварианты удовлетворены? </p>