在GetHashCode()邪恶中使用F#的哈希函数?

Abe*_*bel 4 hash f# equality gethashcode

我在网上遇到过几个代码看起来像这样的地方:

[<CustomEquality;NoComparison>]
type Test =
    | Foo
    | Bar
    override x.Equals y = 
        match y with
        | :? Test as y' ->
            match y' with
            | Foo -> false
            | Bar -> true    // silly, I know, but not the question here
        | _ -> failwith "error"   // don't do this at home

    override x.GetHashCode() = hash x
Run Code Online (Sandbox Code Playgroud)

但是当我在FSI中运行上面的操作时,当我调用hash foo实例Testfoo.GetHashCode()直接调用时,提示不会返回.

let foo = Test.Foo;;
hash foo;;   // no returning to the console until Ctrl-break
foo.GetHashCode();;  // no return
Run Code Online (Sandbox Code Playgroud)

我无法轻易证明它,但它表明对对象的hash x调用GetHashCode(),这意味着上面的代码是危险的.或者仅仅是FSI在玩吗?

我认为上面的代码只是意味着"请实现自定义相等,但将哈希函数保留为默认值".

我同时以不同的方式实现了这种模式,但我仍然想知道我是否正确假设hash只是调用GetHashCode(),导致一个永恒的循环.


顺便说一句,在FSI中使用相等性会立即返回,这表明它GetHashCode()在比较之前不会调用,或者它会执行其他操作.更新:这是有道理的,因为在上面的示例x.Equals中没有调用GetHashCode(),而相等运算符调用Equals,而不是调用GetHashCode().

JLR*_*she 5

如果GetHashCode()重写该方法,则hash运算符将使用:

[ hash运算符是]通用散列函数,旨在根据=运算符为相等的项返回相等的散列值.默认情况下,它将对F#union,record和tuple类型使用结构散列,散列该类型的完整内容.通过为每种类型实现System.Object.GetHashCode,可以在逐个类型的基础上调整函数的确切行为.

所以,是的,这是一个坏主意,它会导致无限循环.


The*_*ght 5

它并不像hash函数只是一个包装器那么简单,GetHashCode但我可以很容易地告诉你,使用这个实现绝对不安全:override x.GetHashCode() = hash x.

如果你追踪这个hash功能,你最终会到这里:

let rec GenericHashParamObj (iec : System.Collections.IEqualityComparer) (x: obj) : int =
    match x with 
    | null -> 0 
    | (:? System.Array as a) -> 
        match a with 
        | :? (obj[]) as oa -> GenericHashObjArray iec oa 
        | :? (byte[]) as ba -> GenericHashByteArray ba 
        | :? (int[]) as ba -> GenericHashInt32Array ba 
        | :? (int64[]) as ba -> GenericHashInt64Array ba 
        | _ -> GenericHashArbArray iec a 
    | :? IStructuralEquatable as a ->    
        a.GetHashCode(iec)
    | _ -> 
        x.GetHashCode()
Run Code Online (Sandbox Code Playgroud)

你可以在这里看到通配符案例调用x.GetHashCode(),因此很有可能发现自己处于无限递归状态.

我可以看到你可能想要hash在一个实现中使用的唯一情况GetHashCode()是当你手动散列一些对象的成员来产生哈希码时.

Don Syme的WebLog中有一个(非常古老的)以这种方式使用hash内部的例子.GetHashCode()


顺便说一下,这并不是你发布的代码唯一不安全的东西.

覆盖object.Equals绝对不能抛出异常.如果类型不匹配,则返回false.这清楚地记录在中System.Object.

Equals的实现不得抛出异常; 他们应该总是返回一个值.例如,如果obj为null,则Equals方法应返回false而不是抛出ArgumentNullException.

(来源)