3

I have the following example class:

public sealed class MyDictKey
{
    public int Type { get; }
    public int SubType { get; }

    public MyDictKey(int type, int subType) // both can only be positive values
    {
        Type = type;
        SubType = subType;
    }

    public override bool Equals(object obj)
    {
        if (obj is MyDictKey other)
        {
            bool typeEqual = other.Type == Type;
            bool subTypeEqual = other.SubType == -1 || SubType == -1 || other.SubType == SubType;
            return typeEqual && subTypeEqual;
        }

        return false;
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int hash = 17;
            hash = hash * 23 + Type.GetHashCode();
            return hash;
        }
    }
}

And the following test (NUnit, if anyone is interested):

    [Test]
    public void CalculatorTest()
    {
        Dictionary<MyDictKey, string> myTypeProcessors = new Dictionary<MyDictKey, string>();

        myTypeProcessors.Add(new MyDictKey(10, 20), "10.20_processor");
        myTypeProcessors.Add(new MyDictKey(3, 4), "3.4_processor");
        myTypeProcessors.Add(new MyDictKey(4, -1), "4.any_processor");
        // -1 means it can process "any" subtype

        for (int i = 0; i < 1000; i++) // should work for any positive number
        {
            bool canGet = myTypeProcessors.TryGetValue(new MyDictKey(4, i), out string value);
            Assert.IsTrue(canGet);
            Assert.That(value, Is.EqualTo("4.any_processor"));

            bool canGet2 = myTypeProcessors.TryGetValue(new MyDictKey(10, i), out string value2);
            if (i == 20)
            {
                Assert.IsTrue(canGet2);
                Assert.That(value2, Is.EqualTo("10.20_processor"));
            }
            else
            {
                Assert.IsFalse(canGet2);
            }
        }
    }

Can I somehow reach the same mechanism only by using the GetHashCode? Since this way, if only the SubTypes differ, the dictionary's TryGetValue will always call into the Equals method. It is important that the new method must not be slower than the original one.

I was thinking of bitwise operators; or are there any magical mathematical formulas for this?

Thank you in advance.

Károly Ozsvárt
  • 1,025
  • 1
  • 12
  • 24
  • You can make GetHashCode return whatever you want, e.g. `Type << 32 | Subtype` – Ben Aug 22 '18 at 21:28
  • it not possible with standard Dictionary, but it possible if you make custom realization with custom hash code comparation. But be careful, you should add value with joker second type to all buckets with same hash of primary type. so it waste memory and can be slower – gabba Aug 22 '18 at 22:07
  • if you make this experiment, please provide a results its will very much appreciated – gabba Aug 22 '18 at 22:11

1 Answers1

4

That's not even a well-defined equality function, as it fails to be transitive.

EG A=(1,1), B =(1,-1), C=(1,2)

A=B, B=C, but not A=C

Consider:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

    namespace ConsoleApp5
    {
        class Program
        {
            static void Main(string[] args)
            {
                var A = new MyDictKey(1, 1);
                var B = new MyDictKey(1, -1);
                var C = new MyDictKey(1, 2);

                var h = new HashSet<MyDictKey>();
                h.Add(A);
                h.Add(B);
                h.Add(C);
                Console.WriteLine(h.Count); //outputs 2


                var h2 = new HashSet<MyDictKey>();
                h2.Add(B);
                h2.Add(C);
                h2.Add(A);
                Console.WriteLine(h2.Count); //outputs 1

                Console.ReadKey();

            }

            public sealed class MyDictKey
            {
                public int Type { get; }
                public int SubType { get; }

                public MyDictKey(int type, int subType) // both can only be positive values
                {
                    Type = type;
                    SubType = subType;
                }

                public override bool Equals(object obj)
                {
                    if (obj is MyDictKey other)
                    {
                        bool typeEqual = other.Type == Type;
                        bool subTypeEqual = other.SubType == -1 || SubType == -1 || other.SubType == SubType;
                        return typeEqual && subTypeEqual;
                    }

                    return false;
                }

                public override int GetHashCode()
                {
                    unchecked
                    {
                        int hash = 17;
                        hash = hash * 23 + Type.GetHashCode();
                        return hash;
                    }
                }
            }
        }
    }
David Browne - Microsoft
  • 80,331
  • 6
  • 39
  • 67
  • 1
    You are right. I am aware of this, but this (the way I exapleined) is the exact way I want it to work like. Do you have any other recommendations? – Károly Ozsvárt Aug 22 '18 at 21:46
  • 2
    @KárolyOzsvárt: The Dictionary class **requires** an Equals method that's transitive. The MyDictKey.Equals method isn't transitive (you can't define an Equals method that ignores a member *some of the time*), so you can't use the class as the dictionary key. If you want it to work a different way, then implement your own lookup logic instead of using Dictionary. – Michael Liu Aug 22 '18 at 21:48