简体   繁体   English

实现 IEqualityComparer 时 GetHashCode 是否应该检查 null?

[英]When Implementing IEqualityComparer Should GetHashCode check for null?

When implementing IEqualityComparer<Product> ( Product is a class), ReSharper complains that the null check below is always false:在实现IEqualityComparer<Product>Product是一个类)时,ReSharper 抱怨下面的空检查总是错误的:

public int GetHashCode(Product product)
{
  // Check whether the object is null. 
  if (Object.ReferenceEquals(product, null))
    return 0;

  // ... other stuff ...
}

(Code example from MSDN VS.9 documentation of Enumerable.Except ) (来自Enumerable.Except 的 MSDN VS.9 文档的代码示例)

ReSharper may be wrong, but when searching for an answer, I came across the official documentation for IEqualityComparer<T> which has an example where null is not checked for: ReSharper 可能是错误的,但是在寻找答案时,我遇到了IEqualityComparer<T>官方文档,其中有一个示例,其中未检查 null:

public int GetHashCode(Box bx)
{
    int hCode = bx.Height ^ bx.Length ^ bx.Width;
    return hCode.GetHashCode();
}

Additionally, the documentation for GetHashCode() states that ArgumentNullException will be thrown when "The type of obj is a reference type and obj is null."此外, GetHashCode()的文档指出,当“obj 的类型是引用类型并且 obj 为 null”时,将抛出ArgumentNullException

So, when implementing IEqualityComparer should GetHashCode check for null, and if so, what should it do with null (throw an exception or return a value)?那么,在实现IEqualityComparerGetHashCode是否应该检查 null,如果是,它应该如何处理 null(抛出异常或返回值)?

I'm interested most in .NET framework official documentation that specifies one way or another if null should be checked.我对 .NET 框架官方文档最感兴趣,该文档指定了是否应检查 null 的一种或另一种方式。

ReSharper is wrong. ReSharper 是错误的。

Obviously code you write can call that particular GetHashCode method and pass in a null value.显然,您编写的代码可以调用该特定的GetHashCode方法并传入一个null值。 All known methods might ensure this will never happen, but obviously ReSharper can only take existing code (patterns) into account.所有已知的方法都可能确保这永远不会发生,但显然 ReSharper 只能考虑现有代码(模式)。

So in this case, check for null and do the "right thing".所以在这种情况下,检查null并做“正确的事情”。


Corollary: If the method in question was private, then ReSharper might analyze (though I'm not sure it does) the public code and verify that there is indeed no way that this particular private method will be called with a null reference, but since it is a public method, and one available through an interface, then推论:如果有问题的方法是私有的,那么 ReSharper 可能会分析(尽管我不确定它会)公共代码并验证确实无法使用null引用调用这个特定的私有方法,但是由于它是一种公共方法,可以通过接口获得,然后

ReSharper is wrong. ReSharper 是错误的。

The documentation says that null values should never be hashable, and that attempting to do so should always result in an exception.文档空值永远不应该是可散列的,并且尝试这样做应该总是导致异常。

Of course, you're free to do whatever you want.当然,你可以自由地做任何你想做的事。 If you want to create a hash based structure for which null keys are valid, you're free to do so, in this case you should simply ignore this warning.如果你想创建一个空键有效的基于散列的结构,你可以自由地这样做,在这种情况下你应该忽略这个警告。

ReSharper has some special case code here. ReSharper 在这里有一些特殊情况代码。 It will not warn about the ReferenceEquals in this:它不会在此警告 ReferenceEquals:

if (ReferenceEquals(obj, null)) { throw new ArgumentNullException("obj"); }

It will warn about the ReferenceEquals in this:它会警告这里的 ReferenceEquals:

if (ReferenceEquals(obj, null)) { return 0; }

Throwing an ArgumentNullException exception is consistent with the contract specified in IEqualityComparer(Of T).GetHashCode抛出 ArgumentNullException 异常与IEqualityComparer(Of T).GetHashCode 中指定的契约一致

If you go to the definition of IEqualityComparer (F12) you'll also find further documentation:如果您转到IEqualityComparer (F12) 的定义,您还会找到更多文档:

    // Exceptions:
    //   System.ArgumentNullException:
    //     The type of obj is a reference type and obj is null.
    int GetHashCode(T obj);

So ReSharper is right that there is something wrong, but the error displayed doesn't match the change you should make to the code.所以 ReSharper 是正确的,有一些错误,但显示的错误与您应该对代码所做的更改不匹配。

There is some nuance to this question.这个问题有一些细微差别。

The docs state that IEqualityComparer<T>.GetHashCode(T) throws on null input ;文档指出IEqualityComparer<T>.GetHashCode(T)抛出null输入 however EqualityComparer<>.Default - which is almost certainly by far the most used implementation - does not throw.然而EqualityComparer<>.Default - 几乎可以肯定是迄今为止最常用的实现 - 不会抛出。

Clearly, an implementation does not need to throw on null it merely has the option too.显然,一个实现不需要抛出 null 它也只是有选项。

However, I'd argue that no implementation should ever throw on null here, it's just confusing, and a possible source of bugs.但是,我认为任何实现都不应该在这里抛出 null,这只是令人困惑,并且可能是错误的来源。 Exceptions are a pain in any case, being a non-local control flow mechanism, and that alone argues for using them when necessary only (ie: not here).异常在任何情况下都是一种痛苦,它是一种非本地控制流机制,仅在必要时才使用它们(即:不在此处)。 But additionally, for IEqualityComparer specifically, the docs state that whenever Equals(x, y) then GetHashCode(x) should equal GetHashCode(y) - and Equals does allow nulls, and is not documented as throwing any exceptions.但另外,对于 IEqualityComparer,文档声明每当Equals(x, y)然后GetHashCode(x)应该等于GetHashCode(y) - 并且Equals确实允许空值,并且没有记录为抛出任何异常。

The invariant that equality implies hashcode equality makes implementing things relying on those hashcodes much simpler.相等意味着哈希码相等的不变性使得依赖于这些哈希码的实现变得更加简单。 Having a gotcha with the null value is a design cost you should avoid paying without need.有一个带有null值的陷阱是一种设计成本,你应该避免在不需要的情况下支付。 And here there is no need, ever.在这里,永远不需要。

In short:简而言之:

  • do not throw from GetHashCode , even though it is allowed不要从 GetHashCode 抛出,即使它是允许的
  • and do check for nulls ;检查空值 Resharper's warning is incorrect. Resharper 的警告是不正确的。

Doing this results in simpler code with fewer gotchas, and it follows the behavior of EqualityComparer<>.Default which is the most common implementation used.这样做会导致代码更简单,问题更少,并且它遵循EqualityComparer<>.Default的行为,这是最常用的实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM