如何在结构中执行契约?

本文关键字:执行 契约 结构 | 更新日期: 2023-09-27 18:15:48

我想强制一个结构对于某个契约总是有效的,由构造函数强制执行。但是,default操作符违反了合同。

考虑以下内容,例如:

struct NonNullInteger
{
    private readonly int _value;
    public int Value
    {
        get { return _value; }
    }
    public NonNullInteger(int value)
    {
        if (value == 0)
        {
            throw new ArgumentOutOfRangeException("value");
        }
        _value = value;
    }
}
// Somewhere else:
var i = new NonNullInteger(0); // Will throw, contract respected
var j = default(NonNullInteger); // Will not throw, contract broken

作为一种解决方法,我将结构体更改为类,这样我就可以确保在初始化新实例时总是调用构造函数。但我想知道,是否有绝对没有办法获得与结构体相同的行为?

如何在结构中执行契约?

我不明白你怎么能做到这一点,因为,与类不同,结构体总是有一个默认的无参数构造函数;给定结构的编写方式,不能阻止值为0:

结构不能包含显式无参数构造函数。结构体成员被自动初始化为默认值。

一种方法是安排事情,使默认值满足契约:

struct NonNullInteger
{
    private readonly int _valueMinusOne;
    public int Value
    {
        get { return _valueMinusOne + 1; }
    }
    public NonNullInteger(int value)
    {
        if (value == 0)
        {
            throw new ArgumentOutOfRangeException("value");
        }
        _valueMinusOne = value - 1;
    }
}

在这种情况下,不可变的类更可取,因为默认状态是无效的。就内存使用而言,它的成本要高一些,但这应该无关紧要,除非您使用非常大量的内存。实际上,"非零数"约束可能最好在每个方法的契约级别处理,而不是放在类中。

如果你真的想强制契约,把异常放在Value getter中而不是构造函数中。然后约定是,如果它确实包含0值,就会抛出异常;这里唯一真正的好处是您永远不会静默地使用零值。缺点是每次使用该值时都要进行比较。

当你不能完全达到你想要的效果时,你可以在getter中验证:

public int Value
{
    get 
    { 
        if (_value == 0) 
            throw new InvalidOperationException("Use of uninitialized struct"); 
        return _value; 
    }
}