如何处理空值当比较值对象的相等性?

5

注意:我使用C#作为例子,但在Java和许多其他语言中,问题实际上是相同的。

假设您实现了一个值对象(如M. Fowler的值对象模式),它有一些可为空的字段:

class MyValueObject
{   
    // Nullable field (with public access to keep the example short):
    public string MyField;
}

当覆盖 Equals() 方法时,如果两个值对象的 MyField 都设置为 null,你应该如何处理?它们相等还是不相等?

在 C# 中,把它们视为相等似乎很明显,因为:

  1. This is the behaviour of Equals() when you use a C# struct instead of a class and do not override Equals().

  2. The following expressions are true:

    null == null
    object.ReferenceEquals(null, null)
    object.Equals(null, null)
    
然而,在SQL中(至少在SQL Server方言中),NULL = NULL 为false,而NULL is NULL 为true 。我想知道在使用O / R映射器(在我的情况下是NHibernate)时期望什么实现。如果你实现“自然”的C#相等语义,当O / R映射器将它们映射到数据库时会有任何不良影响吗?或者允许值对象中的可空字段本来就是错误的?

1
上帝啊,帮助我理解为什么SQL强制人们使用“is null”来表示“等于null”,而不是使用完全正常的“=”运算符。在C#和大多数其他语言中,如果两个字段都为空,则对象相等。(在SQL中也是如此!只是他们强制你使用“is”而不是“=”) - Kirk Woll
我认为 SQL 中 NULL 相等的行为是 NULL 可以是任何东西。它没有明确定义的“真实世界”语义。因此,一个 NULL 可能“实际上”是缺少的 'foo',而另一个 NULL 是缺少的 'bar'。然后,在比较时,SQL 无法知道两个 NULL 是否相等,并且表现保守,这导致它不将 NULL 视为相等。它也不认为它们不相等!在 C# 中,null 简单地是一个空/缺失的引用。 - Marco Eckstein
@Kirk,因为按照定义,与NULL的任何比较都是false。在关系代数中,NULL表示你不知道。所以,如果我不知道我会死的那一天,而你也不知道你会死的那一天,这并不意味着我们会在同一天死去。 - James Anderson
3个回答

1

由于ORM了解关系模型,它们通常会提供使用SQL语义查询的方式。

例如,NHibernate在HQL中提供is [not] null运算符,在Criteria中提供Restrictions.Is[Not]Null

当然,这些范式碰撞的API是存在的:LINQ。大多数ORM尝试在与null比较时做正确的事情(即用is null替换),尽管有时可能会出现问题,特别是行为不明显的情况。


0

就我个人而言,如果它可以为空(在无错误代码的情况下),那么它们应该被视为相等。 但是,如果它不应该为空(例如:客户姓名或交付街道地址),那么它在第一时间就不应该为空。


0

我认为你有两个问题:

一是你需要知道一个MyValueObject实例是否等于另一个实例。

其次,如何将其转换为持久性。

我认为你需要分别考虑这些问题,因为你的角度似乎将它们耦合得太紧密了,这在我看来违反了一些DDD原则 - 领域不应该知道/关心持久性。

如果你不确定MyFieldnull值的影响,可以采取以下措施:(a)让它返回与string不同的类型;(b)让它返回string的派生类,如EmptyString(或类似的特殊情况实现);(c)或者重写Equals方法,并指定这些实例相等的确切含义。

如果你的ORM无法将涉及MyValueObject的特定表达式翻译成SQL,那么也许在持久层中进行更难的工作(使比较发生在SQL翻译之外-是的,我知道会有性能问题,但我相信不是不可能解决的),以保持你的领域模型的清晰。对我来说,解决方案似乎应该源于“对领域模型最好的是什么”。

@James Anderson提出了一个很好的观点。将null保留为错误和失败状态。我认为特殊情况似乎越来越合适。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接