4

I am pretty sure that my problem comes from a really stupid mistake but I can not find it...

I am using a custom class as the key in a SortedList(I also tried SortedDictionary). The first item gets added without a problem but when I try to add a second Item ContainsKey() returns true.

The Class I am using as a Key overrrides Equals() and GetHashCode(). I checked what items actually get compared and that's what I found out: Manually calling Equals() to compare two items works just fine but when it gets called via ContainsKey the object compares itself with the same or another instance of itself. I made sure to check if the object to be added is indeed a new one and it is...

This is the Key-Class

using System;
using System.Collections;
using System.Collections.Generic;
using TFVR.Components.Gaia.Content.Element;
using UnityEngine;

namespace TFVR.Components.Gaia.Content.QueueUi
{
    public class QueueEntryElement : IComparable
    {
        public string Created;
        public string ProductId;
        public ContactInformationElement ContactInformation;
        public int Priority;
        public string OrderId;

        public QueueEntryElement(string created, string productId
            , ContactInformationElement contactInformation, int priority
            , string orderId)
        {
            Created = created;
            ProductId = productId;
            ContactInformation = contactInformation;
            Priority = priority;
            OrderId = orderId;
        }

        public int CompareTo(object obj)
        {
            if (obj == null) return 1;

            QueueEntryElement otherQueueEntryElement = obj as QueueEntryElement;
            if (otherQueueEntryElement != null)
                return this.Priority.CompareTo(otherQueueEntryElement.Priority);
            else
                throw new ArgumentException("Object is not a QueueEntryElement");
        }
        public override bool Equals(object obj)
        {
            if ((obj == null) || !this.GetType().Equals(obj.GetType()))
            {
                return false;
            }
            else
            {
                QueueEntryElement e = (QueueEntryElement)obj;
                return (this.OrderId == e.OrderId);
            }
        }
        public override int GetHashCode()
        {
            return OrderId.GetHashCode();
        }
        public override string ToString()
        {
            string str = "Created: "
                + Created + ", "
                + "Product Id: "
                + ProductId + ", "
                + "Contact Information: "
                + "{" + ContactInformation + "}" + ", "
                + "Priority: "
                + Priority + ", "
                + "Order Id: "
                + OrderId;
            return str;
        }
    }
}

This is the code where I am trying to add to the SortedList

SortedList<QueueEntryElement, string> dict = new SortedList<QueueEntryElement, string>();
private void add(QueueEntryElement q, string 
{
        if (!dict.ContainsKey(q))
        {
            dict.Add(q, s);
        }
}

ContactInformationElement c1 = new ContactInformationElement("a","b","c","d","e");
QueueEntryElement e1 = new QueueEntryElement("a","b", c1, 0,"123");

ContactInformationElement c2 = new ContactInformationElement("f", "g", "h", "i", "j");
QueueEntryElement e2 = new QueueEntryElement("c", "d", c2, 0, "234");

add(e1,"one");
add(e2,"two");
Awrange
  • 43
  • 4
  • 1
    Have a look at this question: https://stackoverflow.com/questions/13262106/dictionary-containskey-how-does-it-work – Matt.G Jan 22 '20 at 21:08
  • 1
    The problem here is that SortedList.ContainsKey uses CompareTo... NOT Equals to determine existence. This means that you are basically using Priority as the Key NOT OrderId. So for the purpose of your example the actual key is Priority. So if your items do not have unique priority values then they will not be added to the "dictionary". This is the normal behavior of the C# Generic SortedList. – Jonathan Alfaro Jan 22 '20 at 21:26
  • 1
    @Matt.G that link refers to Generic Dictionary not to Generic Sorted List. So it does not answer the question at all. – Jonathan Alfaro Jan 22 '20 at 21:34
  • 2
    @Darkonekt Great catch, I had no idea about that, and the documentation doesn't seem to mention that specifically. I still think the question should include example values for access to quick unambiguous reproduction. But, with the problem being able to be reproduced, I'm voting to reopen. – Ruzihm Jan 22 '20 at 21:57
  • 1
    @Darkonekt Thank you! That was just the information I needed! I added another condition to the CompareTo() so that Priority gets checked first and orderId(unique) gets compared second if Priority values are identical. – Awrange Jan 22 '20 at 23:56
  • 1
    @Awrange Can you remove your answer from the question and add it as an answer below? You can then accept your answer and get the rep. Let's keep the Q&A separate. –  Jan 23 '20 at 13:59

2 Answers2

4

The problem here is that SortedList.ContainsKey uses CompareTo... NOT Equals to determine existence.

This means that you are basically using Priority as the Key NOT OrderId.

So for the purpose of your example the actual key is Priority.

So if your items do not have unique priority values then they will not be added to the "dictionary".

This is the normal behavior of the C# Generic SortedList.

Jonathan Alfaro
  • 4,013
  • 3
  • 29
  • 32
0

I added some dummy code just in case anyone is still interested in testing. To solve my problem i simply changed my CompareTo() to this:

public int CompareTo(QueueEntryElement obj)
        {
            if (obj == null) return 1;
            QueueEntryElement otherQueueEntryElement = obj as QueueEntryElement;
            if (otherQueueEntryElement != null)
            {
                if (Priority.CompareTo(otherQueueEntryElement.Priority) == 0)
                {
                    return OrderId.CompareTo(otherQueueEntryElement.OrderId);
                }
                return 0;
            }
            else
                throw new ArgumentException("Object is not a QueueEntryElement");
        }