当实现IEqualityComparer<Product>
(Product
是一个类)时,ReSharper会抱怨下面的空检查始终为false:
public int GetHashCode(Product product)
{
// Check whether the object is null.
if (Object.ReferenceEquals(product, null))
return 0;
// ... other stuff ...
}
(来自MSDN VS.9 documentation of Enumerable.Except的代码示例)
ReSharper可能是错的,但是在搜索答案时,我遇到了official documentation for IEqualityComparer<T>
,其中有一个未检查null的示例:
public int GetHashCode(Box bx)
{
int hCode = bx.Height ^ bx.Length ^ bx.Width;
return hCode.GetHashCode();
}
此外,当{obj的类型是引用类型且obj为null时,GetHashCode()
states将会抛出ArgumentNullException
的文档。
因此,当实现IEqualityComparer
时,GetHashCode
应检查null,如果是,那么它应该用null做什么(抛出异常或返回值)?
我最感兴趣的是.NET框架官方文档,如果要检查null,就会指定这种或那种方式。
答案 0 :(得分:9)
ReSharper错了。
显然,您编写的代码可以调用该特定GetHashCode
方法并传入null
值。所有已知的方法可能会确保这种情况永远不会发生,但显然ReSharper只能考虑现有的代码(模式)。
因此,在这种情况下,请检查null
并执行“正确的事情”。
推论:如果有问题的方法是私有的,那么ReSharper可能会分析(虽然我不确定它)公共代码并验证确实没有办法用{{调用这个特定的私有方法1}}引用,但因为它是一个公共方法,并且可以通过接口获得,所以
ReSharper错了。
答案 1 :(得分:3)
文档表示,空值永远不应该是可清除的,并且尝试这样做应该总是导致异常。
当然,你可以自由地做任何你想做的事。如果你想创建一个基于哈希的结构,其中空键有效,你可以自由地这样做,在这种情况下你应该忽略这个警告。
答案 2 :(得分:0)
ReSharper在这里有一些特殊的案例代码。它不会对此中的ReferenceEquals发出警告:
if (ReferenceEquals(obj, null)) { throw new ArgumentNullException("obj"); }
它将在此警告ReferenceEquals:
if (ReferenceEquals(obj, null)) { return 0; }
抛出ArgumentNullException异常与IEqualityComparer(Of T).GetHashCode中指定的契约一致
如果您转到IEqualityComparer
(F12)的定义,您还可以找到更多文档:
// Exceptions:
// System.ArgumentNullException:
// The type of obj is a reference type and obj is null.
int GetHashCode(T obj);
所以ReSharper说错了,但显示的错误与您应该对代码所做的更改不符。
答案 3 :(得分:0)
这个问题有些细微之处。
文档指出IEqualityComparer<T>.GetHashCode(T)
throws on null
input;但是EqualityComparer<>.Default
-几乎可以肯定是迄今为止最常用的实现-不会抛出。
很显然,一个实现也不需要将其抛出null,它也只有该选项。
但是,我认为这里没有实现应该抛出null,这只是令人困惑,而且可能是bug的来源。在任何情况下,异常都是一种痛苦,因为它是一种非本地的控制流机制,仅此一项就主张仅在必要时使用它们(即:此处不行)。但此外,对于IEqualityComparer而言,文档指出,无论何时Equals(x, y)
时GetHashCode(x)
应该等于GetHashCode(y)
-和Equals
确实允许为空,并且没有记录为引发任何异常。
相等意味着哈希码相等的不变性使得依赖于那些哈希码的事情变得更加简单。拥有null
值的陷阱是一项设计成本,您应避免在不需要时支付。而且这里永远都不需要。
简而言之:
这样做可以使代码变得更简单,陷阱更少,并且遵循EqualityComparer<>.Default
的行为,这是最常用的实现。