Home > Software design >  Reading info from Dictionary
Reading info from Dictionary

Time:09-28

I am learning about dictionaries, and lost in this problem.

class Program
{
    static void Main(string[] args)
    {
        Dictionary<A, B> dict = new Dictionary<A, B>();
        A a = new A { A1 = 1, A2 = 2 };
        B b = new B { B1 = 3, B2 = "foo" };
        dict.Add(a, b);
        A c = new A { A1 = 1, A2 = 2 };

        Console.WriteLine($"dict.ContainsKey(a): {dict.ContainsKey(a)}");
        Console.WriteLine($"dict.ContainsKey(c): {dict.ContainsKey(c)}");
        Console.ReadLine();
    }
}
class A
{
    public int A1 { get; set; }
    public int A2 { get; set; }
}

class B
{
    public int B1 { get; set; }
    public string  B2 { get; set; }
}

output for this code is:

dict.ContainsKey(a): True
dict.ContainsKey(c): False

What I want to achieve is get the value of B2, based on the parameters A1 and A2. I was hoping that dict.ContainsKey(c) returned true, but it does not for some reason.

I could do something like this, which returns foo, but that seems to be overkill ?:

(from f in dict.Keys where f.A1==1 && f.A2==2 select dict[f].B2).First()

How can I get the value for B2, If I know a value for A1 and A2 ?

CodePudding user response:

When comparing custom classes, you should explain .net how to do it with a help of Equals and GetHashCode methods. If you don't do it, .net will compare references, not values. In your case it can be

class A {
  public int A1 { get; set; }
  public int A2 { get; set; }

  public override bool Equals(object obj) =>
    (obj is A other) && (A1 == other.A1) && (A2 == other.A2); 
  
  public override int GetHashCode() => unchecked((A1 << 16) ^ A2); 

  // For debugging
  public override string ToString() => $"A1 = {A1}; A2 = {A2}";
}

Now

 Dictionary<A, B> dict = new Dictionary<A, B>() {
   {new A { A1 = 1, A2 = 2 }, new B { B1 = 3, B2 = "foo" }},
 };

 A c = new A { A1 = 1, A2 = 2 };

 if (dict.ContainsKey(c))
   Console.WriteLine($"Contains {c}");
 else
   Console.WriteLine($"Doesn't contains {c}"); 

CodePudding user response:

You need to define a class, which implements the IEqualityComparer to define how to compare keys. You want to compare them based on their properties rather than their references.

class AValueComparer : IEqualityComparer<A>
{
    public bool Equals(A x, A y) => x.A1 == y.A1 && x.A2 == y.A2;
    public int GetHashCode([DisallowNull] A obj) => HashCode.Combine(obj.A1, obj.A2);
}

Then all you need is to connect this comparer to your dictionary.

[Fact]
public void Test1()
{
    //Arrange
    var dict = new Dictionary<A, B>(new AValueComparer());
    var a = new A { A1 = 1, A2 = 2 };
    var b = new B { B1 = 3, B2 = "foo" };
    dict.Add(a, b);
    var c = new A { A1 = 1, A2 = 2 };

    //Act
    var doesAExist = dict.ContainsKey(a);
    var doesCExist = dict.ContainsKey(c);

    //Assert
    Assert.Equal(doesAExist, doesCExist);
}

CodePudding user response:

It's false because classes in C# are reference types, and while a and c have the same values (i.e A1 and A2 are the same) they are not the same instance, which is what's checked by default. If you want to you can implement a IEqualityComparer which equates A with its values and pass a new instance of that to the Dictionary constructor

  • Related