-
-
Notifications
You must be signed in to change notification settings - Fork 32
Open
Description
Describe the bug
I noticed that using SimpleJSON.JSONBool as a dictionary key: after adding an instance as a key, ContainsKey(sameInstance) returns false even before any mutation. This appears to be because JSONBool.Equals(object) returns true only when compared to a boxed bool, and returns false when compared to a JSONBool (including itself).
Several Issues:
JSONBool.Equalsis not reflexive (x.Equals(x)can be false), which breaks core .NET equality and collection semantics immediately.JSONBool/JSONString/JSONNumberhave hash codes derived from mutable state, so they’re unsafe in hash-based scenarios after mutation (including implicit hashing in LINQ- maybe-by-design???: cross-type equality (
JSONNumbervs numeric primitives,JSONStringvsstring) is inherently non-symmetric, so it should either be avoided, carefully documented, or implemented via explicit comparisons/conversions rather thanEquals
Mutable hash (JSONBool/JSONString/JSONNumber)
This can break any hash-based collection or algorithm if the node is mutated after being used as a key/element, for example:
HashSet<JSONBool>membership can "lose" an item after mutation for the same reason a dictionary key can
- LINQ methods that build internal hash sets/lookups (e.g., Distinct, Except, Intersect, GroupBy, ToLookup) can behave inconsistently if elements/keys mutate while participating
To Reproduce
small repro:
using System;
using System.Collections.Generic;
using SimpleJSON;
public static class Program
{
public static void Main()
{
const Int32 val = 1;
SimpleJSON.JSONBool jsonBool = new SimpleJSON.JSONBool(true);
Dictionary<SimpleJSON.JSONBool, Int32> dict = new Dictionary<SimpleJSON.JSONBool, Int32>();
dict.Add(jsonBool, val);
Int32 hash1 = jsonBool.GetHashCode();
Console.WriteLine("hashcode: " + hash1);
Console.WriteLine("containsKey: " + dict.ContainsKey(jsonBool));
Console.WriteLine("containsValue: " + dict.ContainsValue(val));
jsonBool.AsBool = false; // mutate the object in the dict to lose it
Int32 hash2 = jsonBool.GetHashCode();
Console.WriteLine("hashcode: " + hash2);
Console.WriteLine("containsKey: " + dict.ContainsKey(jsonBool));
Console.WriteLine("containsValue: " + dict.ContainsValue(val));
}
}actual output:
hashcode: 1
containsKey: False
containsValue: True
hashcode: 0
containsKey: False
containsValue: True
Expected behavior
dict.ContainsKey(jsonBool)should return true immediately afterdict.Add(jsonBool, val)when using the same instance as the lookup key.x.Equals(x)equalstrue
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels