2 equal objects, but no same containskey result in hash

Hi community,

I’am beginner in powershell.
I found a strange behaviour I can’t explain.

A put a key and a value in a hash table
The key is an object “class Test”
The value is null
Then, I test if containskey with the key I previously added return True. It’s Yes. So it’s perfect
But, I created the same object in a new variable and then I test containskey with this object and the result is false.
I don’t undertand why. It’s the same object. I mean the content is the same.
Please see the code and the result below.

class Test {

    [int]$var



    Test([int]$i){
        $this.var=$i
    }




    [boolean] Equals($i){

        if($i -eq $null){
            return $false
        } else {
            if($this.var -eq $i.var){
                return $true
            } else {
                return $false
            }
        }

    }


    [int] getHashCode(){
        return $this.var
    }
}

$a1 = [Test]::new(1)
$a2 = [Test]::new(1)
Write-output "Test 1     `$a1 -eq `$a2  ???"
Write-output "Should be display True"
$a1.Equals($a2)
Write-output "Perfect"

# Now with hashtable
$h = @{}
$h.Add($a1,$null)

Write-output ""
Write-output "Test 2     `Is hashtable containskey `$a1  ???"
Write-output "Should be display True"
$h.ContainsKey($a1)
Write-output "Perfect"

Write-output ""
Write-output "Test 3     `Is hashtable containskey `$a2 which is equal to `$a1  ???"
Write-output "Should be display True"
$h.ContainsKey($a2)
Write-output "Oups, Why ????"

Write-output ""
Write-output "Test 4     `What the result of -eq operator on `$a1 and `$a2  ???"
$a1 -eq $a2

The result

Test 1 $a1 -eq $a2 ???
Should be display True
True
Perfect

Test 2 Is hashtable containskey $a1 ???
Should be display True
True
Perfect

Test 3 Is hashtable containskey $a2 which is equal to $a1 ???
Should be display True
False
Oups, Why ???

Test 4 What the result of -eq operator on $a1 and $a2 ???
True

Is there a function I missed to override or not well override ?
Thanks for your explanation.

BR

Objects are different even if their keys and values are the same, it’s a different object. You can test this by creating two identical hash tables and testing if they are equal, they will not be. The reason your $a1 equals $a2 is because you’re actually checking the value of the var property inside your custom equals method.

If you just take your $a1 and $2 instances and compare with a normal -eq you should see they are not equal.

Thanks for the explanation.

So, is there another manner to have “complex” data structure to react like simple data (i.e Int, String, etc) ?

I did the test you mention (test $a1 -eq $a2). The result is equal !!! I added a TEST 4 to quickly test on your side if you want.

English is not my native tongue.
I have some difficulties to undertanstand the paragraph below
I mean I don’t undertand the difference before and after the .Net Framework 2.0.
Someone can better explain to me the parapgraph below ?

Source
Hashtable.ContainsKey(Object) Method (System.Collections) | Microsoft Learn

Starting with the .NET Framework 2.0, this method uses the collection’s objects’ [Equals] and [CompareTo] methods on item to determine whether item exists. In the earlier versions of the .NET Framework, this determination was made by using the [Equals] and [CompareTo] methods of the item parameter on the objects in the collection.

You are correct, my apologies. This is because you are overriding the default equals method with a custom one (checks the value of var.) Further expanding your class will reveal what I am referring to.

class Test {

    [int]$var
    [string]$word


    Test([int]$i,[string]$w){
        $this.var=$i
        $this.word = $w
    }




    [boolean] Equals($i){

        if($i -eq $null){
            return $false
        } else {
            if($this.var -eq $i.var){
                return $true
            } else {
                return $false
            }
        }

    }


    [int] getHashCode(){
        return $this.var
    }
}

$a1 = [test]::new(1,'one')
$a2 = [test]::new(1,'two')

$a1 -eq $a2
True

So even though they aren’t equal, your custom equals method only checks the var value. Again, objects are different instances when it comes to the default equals implementation

$b1 = @{var=1}
$b2 = @{var=2}

$b1 -eq $b2
False

Ok, for objects used in keys in hashTable. Instead of using containskey method, I propose to use these lines of code below.

$i = $h.GetEnumerator()
$found = $false
while($i.moveNext()){
    $it = $i.Key
    if($it.equals($a2)){ $found = $true }
}
$found

Personnaly, I choose to replace the object by its gethashcode I override. In this way, through a hash I can rebuild my data structure if I want. And I can use containsKey safely in hashtable because gethashcode return an Int and Int work perfectly with containsKey method of HashTable data structure.

Many thanks for your explanation and time you passed on my problem.
Unfortunatly I don’t succeed to undertand all niceties you explain.
I will use a workaround.

I found my mistake. I wrote “getHashCode” instead of “GetHashCode”. (first letter must be in CAPS) Now it works.

class Test  {

    [int]$var



    Test([int]$i){
        $this.var=$i
    }




    [boolean] Equals ($i){
        return $this.var -eq $i.var
    }


    [int] CompareTo($i){

        if($i -eq $null){
            return $false
        } else {
            return $($this.var -eq $i.var)
        }

    }


    [int] GetHashCode(){
        return $this.var
    }
}

$a1 = [Test]::new(1)
$a2 = [Test]::new(1)
Write-output "Test 1     `$a1 -eq `$a2  ???"
Write-output "Should be display True"
$a1.Equals($a2)
Write-output "Perfect"

# Now with hashtable
$h = @{}
$h.Add($a1,"bingo")
$h.Add(1,"1")


Write-output ""
Write-output "Test 2     `Is hashtable containskey `$a1  ???"
Write-output "Should be display True"
$h.ContainsKey($a1)
Write-output "Perfect"

Write-output ""
Write-output "Test 3     `Is hashtable containskey `$a2 which is equal to `$a1  ???"
Write-output "Should be display True" 
$h.ContainsKey($a2)
Write-output "Oups, Why ????"


Write-output ""
Write-output "Test 4     `What the result of -eq operator on `$a1 and `$a2  ???"
$a1 -eq $a2