0

I have a Dictionary. I use this code to get the index of the object:

type.Name = _shuffledClasses.Where(entry => entry.Key.Name.Equals(originName))
                            .Where(entry => entry.Key.Namespace.Equals(type.Namespace))
                            .Select(item => item.Value).GetEnumerator().Current.Name;

But it doesn't find the Object. I double checked if the Object is create correctly and if it exists, and it does! The Object in question is in the 'Key' column and I wan't to get the object in the 'Value' column.

I tried also this piece of code, which doesn't work aswell:

type.Name = _shuffledClasses[new Classes()
{
     Name = originName,
     Namespace = type.Namespace
}].Name;

My "Classes" Object looks as follows:

class Classes
{
    public string Namespace { get; set; }
    public string Name { get; set; }
}

Why wont it find the Object?

I did some research and I tried overriding Equals and GetHashCode, now my class looks like this and still doesn't work:

public override bool Equals(object obj)
{
    Classes fooItem = obj as Classes;
    return fooItem == this;
}

public override int GetHashCode()
{
    return base.GetHashCode();
}
Gilad Green
  • 36,708
  • 7
  • 61
  • 95
ioncodes
  • 104
  • 3
  • 9

3 Answers3

3

You code is fundamentally broken right here:

.GetEnumerator().Current.Name;

The enumerator needs to be moved one element ahead with MoveNext() or Current will not have any value.

Also, enumerators should be disposed. Try this instead:

type.Name = _shuffledClasses.Where(entry => entry.Key.Name.Equals(originName))
                            .Where(entry => entry.Key.Namespace.Equals(type.Namespace))
                            .Select(item => item.Value)
                            .First().Name;

If you don't want to use First() for whatever reason, you can also iterate manually like this:

using (var enumerator = _shuffledClasses.Where(entry => entry.Key.Name.Equals(originName))
                            .Where(entry => entry.Key.Namespace.Equals(type.Namespace))
                            .Select(item => item.Value).GetEnumerator()) {
    enumerator.MoveNext(); // maybe check here if the operation is successful!
    type.Name = enumerator.Current.Name;
}
Lucero
  • 59,176
  • 9
  • 122
  • 152
  • I thought this before, that's why I tried another solution. Any thoughts on the second version? – ioncodes Oct 15 '16 at 18:04
  • You're welcome. Nevertheless, to look up a dictionary value with a key you should properly make the key class equatable as shown by Marcin and Gilad - the lookup will be **much** more efficient that way! – Lucero Oct 15 '16 at 18:17
2

Why wont it find the Object?

Because you didn't override GetHashCode and Equals in your class, which is required for it to work as key in Dictionary and HashSet

Dictionary<TKey, TValue> requires an equality implementation to determine whether keys are equal. You can specify an implementation of the IEqualityComparer<T> generic interface by using a constructor that accepts a comparer parameter; if you do not specify an implementation, the default generic equality comparer EqualityComparer<T>.Default is used. If type TKey implements the System.IEquatable<T> generic interface, the default equality comparer uses that implementation.

And this generic comparer uses reference equality if your type does not implement IEquatable<T>.

You can try using LINQ and First if you don't want to implement your custom equality, but that will defeat the purpose of using Dictionary<TKey, TValue> because you'll have to scan the entire collection to get that value:

shuffledClasses.First(x => x.Key.Name == myName && x.Key.Value == myValue).Value
MarcinJuraszek
  • 124,003
  • 15
  • 196
  • 263
2

If you are using a Dictionary the clearest way of finding a Key in it is by using the [] or by TryGetValue.

Using linq is great but in my opinion this is not a place to use it. Searching a key in a dictionary is a o(1) operation but using linq the way you did turns it to a o(n). If you can override the GetHashCode and Equals or supply a custom IEquatable<> I think it is better.

static void Main(string[] args)
{
    Dictionary<Classes, string> data = new Dictionary<Classes, string>
    {
        [new Classes { Name = "a", Namespace = "a" }] = "first",
        [new Classes { Name = "b", Namespace = "b" }] = "second",
    };

    var key = new Classes { Name = "a", Namespace = "a" };

    string result1 = data[key]; // "first"


    string result2;
    if (data.TryGetValue(key, out result2))
    {
        Console.WriteLine(result2); // 'first"
    }
}

class Classes
{
    public string Name { get; set; }
    public string Namespace { get; set; }

    public override bool Equals(object obj)
    {
        var other = obj as Classes;
        if (other == null)
            return false;

        return other.Name == Name &&
               other.Namespace == Namespace;
    }

    public override int GetHashCode()
    {
        return Name.GetHashCode() ^
               Namespace.GetHashCode();
    }
}

The problem with your implementation of the overriding of the methods is:

  1. Equals - after casting you are still not actually comparing the properties

    public override bool Equals(object obj)
    {
        Classes fooItem = obj as Classes;
        return fooItem == this;
    }
    
  2. GetHashCode - You gave no implementation to it.. Still using the GetHashCode of object

    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
    

For further references see:

  1. MSDN - Implementing the Equals Method
  2. Stackoverflow - Correct way to override Equals() and GetHashCode()
  3. Stackoverflow - Why is it important to override GetHashCode when Equals method is overridden?
Community
  • 1
  • 1
Gilad Green
  • 36,708
  • 7
  • 61
  • 95