如何在结构中执行契约?
本文关键字:执行 契约 结构 | 更新日期: 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;
}
}