0

I'm an amateur programmer, and this seems like a simple problem to fix, but I just can't figure out how. Below is C# code that isn't acting like I want it to. I expect for this to return 3, yet instead throws a KeyNotFoundException. The Lists are the same, so shouldn't it be returning 3? Thanks for any help.

Dictionary<object, double> dict = new Dictionary<object, double>();
dict.Add(new List<object>() { "a", "b" }, 3);
double output = dict[new List<object>() { "a", "b" }];
Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
vlts
  • 3
  • 1
  • Using a List<> object as the key of a Dictionary is not a good idea, it goes against most general use cases. – EkoostikMartin Jul 18 '13 at 13:57
  • What would be a better replacement? If I have a list of objects, but I don't know how many objects there are or what type they are, how do I map these to numbers? – vlts Jul 18 '13 at 14:02
  • well @Daniel Hilgarth provided you with a decent alternative `Tuple`, but to give a more specific answer you would have to give examples of the type of objects you need to put into the dictionary. – EkoostikMartin Jul 18 '13 at 14:05

3 Answers3

8

List<T> is a reference type without a special Equals implementation. So, in your case, while both of your list instances have the same content, they are still different instances and as such are not considered equal upon key lookup.

Depending on your needs, you could use different solutions:

  1. If you always have the same amount of items in your list, you can use a Tuple:

    Dictionary<Tuple<string, string>, double> dict =
        new Dictionary<Tuple<string, string>, double>();
    dict.Add(Tuple.Create("a", "b"), 3);
    double output = dict[Tuple.Create("a", "b")];
    
  2. If the amount of items differ, you can create your own list that compares its content.

Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
  • Thanks. Is this because of how some objects (like lists) are stored by reference, while others are not? – vlts Jul 18 '13 at 13:57
  • @vlts: This is a rather broad topic, but in short: In .NET there are value types and reference types. Value types are compared by content, reference types are - by default - compared by reference, i.e. two different instances with the same data are *not* considered equal. However, you can override this behavior by overriding `object.Equals` in your class. – Daniel Hilgarth Jul 18 '13 at 13:59
1

Because there are two different objects/instances.

Vladimir Gondarev
  • 1,243
  • 7
  • 14
1

Both lists are separate instances, so ReferenceEquals returns false, but that is used by default. You could implement a custom IEqualityComparer<IList<object>> for the dictionary constructor:

public class ListComparer : IEqualityComparer<IList<object>>
{
    public bool Equals(IList<object> x, IList<object> y)
    {
        if (x == null || y == null) return false;
        return x.SequenceEqual(y);
    }

    public int GetHashCode(IList<object> list)
    {
        if (list == null) return int.MinValue;
        int hash = 19;
        unchecked // Overflow is fine, just wrap
        {
            foreach (object obj in list)
                if(obj != null)
                    hash = hash + obj.GetHashCode();
        }
        return hash;
    }
}

Now it works as expected:

var  dict = new Dictionary<List<object>, double>(new ListComparer());
dict.Add(new List<object>() { "a", "b" }, 3);
double output = dict[new List<object>() { "a", "b" }]; // 3.0
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939