Search code examples
c#.nethashgethashcode

How to successfully hash System.Windows.Input.Key values with modifier key states?


I am trying to write a hashing algorithm that's gonna successfully hash System.Windows.Input.Key values with modifier key states, for instance:

ctrl = false
shift = true
alt = false
capslock = true
numlock = false
scroll lock = false
key: A

So a key value like this should be separated from others with different states for ctrl, shift, alt, etc. But since these are just true or false, I am not sure how to make it distinct for the hash values?

Any ideas? It just have to be unique enough to handle all possible key combinations.


Solution

  • I would build a class containing all values able to compute it's own hash code, like:

        class KeyInfo : IEquatable<KeyInfo>
        {
            public bool Ctrl { get; private set; }
            public bool Shift { get; private set; }
            public bool Alt { get; private set; }
            public bool CapsLock { get; private set; }
            public bool NumLock { get; private set; }
            public bool ScrollLock { get; private set; }
            public Keys Key { get; private set; }
    
            public KeyInfo(bool ctrl, bool shift, bool alt, bool capsLock, bool numLock, bool scrollLock, Keys key)
            {
                this.Ctrl = ctrl;
                this.Shift = shift;
                this.Alt = alt;
                this.CapsLock = capsLock;
                this.NumLock = numLock;
                this.ScrollLock = scrollLock;
                this.Key = key;
            }
    
            public override bool Equals(object obj)
            {
                return this.Equals(obj as KeyInfo);
            }
    
            public bool Equals(KeyInfo other)
            {
                if (other == null)
                    return false;
                return this.Ctrl == other.Ctrl && this.Shift == other.Shift &&
                       this.Alt == other.Alt && this.CapsLock == other.CapsLock &&
                       this.NumLock == other.NumLock && this.ScrollLock == other.ScrollLock &&
                       this.Key == other.Key;
            }
    
            public override int GetHashCode()
            {
                unchecked
                {
                    int hash = 17;
                    hash = hash * 23 + this.Ctrl.GetHashCode();
                    hash = hash * 23 + this.Shift.GetHashCode();
                    hash = hash * 23 + this.Alt.GetHashCode();
                    hash = hash * 23 + this.CapsLock.GetHashCode();
                    hash = hash * 23 + this.NumLock.GetHashCode();
                    hash = hash * 23 + this.ScrollLock.GetHashCode();
                    hash = hash * 23 + this.Key.GetHashCode();
                    return hash;
                }
            }
        }
    

    Credits to this Jon Skeet's answer for the GetHashCode() implementation.

    N.B.

    this class can be effectively used as a Dictionary key, into HashSet or in LINQ Distinct() and other LINQ sets' operations.

    EDIT:

    I'd like to enforce the fact that you mustn't use the hash code as dictionary key, but use the whole class instead.
    You cannot rely on the uniqueness of the hash-codes because hashing it's subjected to collisions.