ioncodes
ioncodes

Reputation: 124

C# Object not found in Dictionary<T, T>

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();
}

Upvotes: 0

Views: 736

Answers (3)

Gilad Green
Gilad Green

Reputation: 37281

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?

Upvotes: 2

Lucero
Lucero

Reputation: 60266

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;
}

Upvotes: 3

MarcinJuraszek
MarcinJuraszek

Reputation: 125650

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

Upvotes: 2

Related Questions