在coders at work中,作者问“如何在代码中使用不变量”。请解释这个问题是什么意思。
我在 wiki 上看到了类不变量,但这个例子是用 Java 编写的,我在 Java 方面的熟练程度不足以将这个例子与 C# 联系起来。.NET 4.0 引入了不变性、协变和逆变,并在此处进行了很好的解释。不变性是如此广泛。作者对该词的使用似乎与单元测试有关。对于读过这本书的人来说,作者是什么意思?我们是在谈论做出假设并在单元测试之后简单地测试有效性吗?
在coders at work中,作者问“如何在代码中使用不变量”。请解释这个问题是什么意思。
我在 wiki 上看到了类不变量,但这个例子是用 Java 编写的,我在 Java 方面的熟练程度不足以将这个例子与 C# 联系起来。.NET 4.0 引入了不变性、协变和逆变,并在此处进行了很好的解释。不变性是如此广泛。作者对该词的使用似乎与单元测试有关。对于读过这本书的人来说,作者是什么意思?我们是在谈论做出假设并在单元测试之后简单地测试有效性吗?
不变这个词并不意味着在某些条件下某些东西不会改变。有许多不同种类的不变量。例如,在物理学中,光速在洛伦兹变换下是不变的,即如果您更改为参考系,它不会改变。在编程中也有很多种不变量。有些类不变量在对象的生命周期内不会改变,方法不变量在函数的生命周期内不会改变,......
类不变量是在该类的实例中始终(至少在可公开观察的时间)为真的东西。
这与协方差/逆变方差无关。Co-/Contra-variance 描述了哪些类型可以替换为具有不同(通用)参数或返回类型的其他类型。虽然您可以称某些东西为不变量,因为它不支持协方差/反方差,但这是一种与类或方法不变量完全不同的不变量。
例如,某种集合可能具有以下不变量:
有了这个类:
class MyCollection<T>
{
private T[] data;
private int size;
public MyCollection()
{
data=new T[4];
}
public int Size{get{return size;}}
public int Capacity{get{return data.Length;}}
[ContractInvariantMethod]
protected void ClassInvariant()
{
Contract.Invariant(data != null);
Contract.Invariant(Size >= 0);
Contract.Invariant(Capacity >= 0);
Contract.Invariant(Size < Capacity);
}
}
几乎每个类都有一些不变量,但不是每个人都强制执行它们。.net 4 添加了一种使用代码契约记录和断言它们的好方法。
在这种情况下,不变量是指适用于参数的条件,并且在函数/方法的整个生命周期中保持为真。
来自维基百科:
在计算机科学中,谓词被称为操作序列的不变量,前提是:如果谓词在序列开始之前为真,则在序列结束时为真。
在 .NET 中,可以使用Code Contracts检查/强制执行不变量。
Wiki 中的那个例子是用 JML 实现不变量,这是一种非常具体的、奇异的、也许是经过深思熟虑的研究技术,但它根本不是主流。此外,它不仅在谈论不变量,而且只是在谈论断言一个 mutator 做了预期的事情,当我想到不变量时,这不是我想到的。我没有读过 Coders at Work,但我怀疑 Coders at Work 中的任何人都使用过 JML。
无论如何,我一直认为不变量是“提前崩溃”的好方法,并且当实际上程序状态处于不合理(计划外)状态时,可以防止您的代码尝试做合理的事情。
C# 代码中不变量的一个很好的例子可能是永远不要将对象提供给 N-Hibernate 进行保存,除非该对象已经传递了它的不变量,其中应该有很多以防止无意义的数据进入数据库。让我们看看我是否能想到任何其他的例子......
假设您有一个 User 类,它总是应该有一个主电子邮件地址属性,那么在保存之前要检查的不变量可能是确保电子邮件地址字段不为空。否则,假设所有用户的电子邮件地址都存在的其他应用程序逻辑可能会在稍后尝试向用户发送电子邮件时搞砸。
进一步假设一个用户对象“有很多”电子邮件对象,并且假设没有拥有用户的电子邮件存在没有任何意义,那么电子邮件类的不变量可能是确保电子邮件对其的引用user 始终不为空,否则您有一个孤立的电子邮件对象,当您尝试引用电子邮件的所有者时,可能会导致空指针异常。
假设您正在编写一个应该以传统 WPF 形式呈现 Amazon S3 对象状态的 GUI。表单上的一个不变量可能是在执行该表单上的任何事件处理程序之前确保表单正确绑定到其对象。
假设您正在编写 StackOverflow。这里的一个不变量可能是确保用户的声誉水平永远不会为负,即使用户正在接受声誉惩罚。负面声誉可能会破坏那些将体验呈现为时间函数的漂亮图表(除非这些图表准备在 0 y 轴下方绘制线条,它们很可能是......)。
至于何时检查不变量,您可以在任何改变数据状态的方法结束时执行此操作。在防御性编程的最激进和偏执的水平上,您可以在所有方法之前和之后检查不变量。
在此上下文中的含义与 C#4 中引入的泛型的 co 和 contra 方差无关,而是与您链接到的 wikipedia 文章具有相同的含义。在 C# 中,这可以是调试断言(即Debug.Assert(condition)
)以及代码契约库。例如ContractInvariantMethodAttribute
,可以将 which 应用于断言类不变量的类中的方法:
[ContractInvariantMethod]
protected void ClassInvariant()
{
Contract.Invariant(someCondition);
}