0

I made a simple multi join code in c#-LINQ.

But some problem happen like this.

sample 1) It is well work. The result is [1, 2].

public class tempClass1
    {
        public int i1;
        public int i2;
    }

    public class tempClass2
    {
        public int i3;
        public int i4;
    }

    public class CompareClass
    {
        public int compare1;
        public int compare2;
    }

    List<tempClass1> tempList1 = new List<tempClass1>();
    List<tempClass2> tempList2 = new List<tempClass2>();


    public MainWindow()
    {
        InitializeComponent();            

        try
        {
            tempList1.Add(new tempClass1() { i1 = 1, i2 = 2 });
            tempList1.Add(new tempClass1() { i1 = 3, i2 = 4 });
            tempList1.Add(new tempClass1() { i1 = 5, i2 = 6 });

            tempList2.Add(new tempClass2() { i3 = 1, i4 = 2 });

            var result = from t1 in tempList1
                         join t2 in tempList2 on
                         new { compare1 = t1.i1, compare2 = t1.i2 } equals
                         new { compare1 = t2.i3, compare2 = t2.i4 }
                         select t1;
        }
        catch(Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }

sample 2) It is not working compare code. The result is empty.

public class tempClass1
    {
        public int i1;
        public int i2;
    }

    public class tempClass2
    {
        public int i3;
        public int i4;
    }

    public class CompareClass
    {
        public int compare1;
        public int compare2;
    }

    List<tempClass1> tempList1 = new List<tempClass1>();
    List<tempClass2> tempList2 = new List<tempClass2>();


    public MainWindow()
    {
        InitializeComponent();            

        try
        {
            tempList1.Add(new tempClass1() { i1 = 1, i2 = 2 });
            tempList1.Add(new tempClass1() { i1 = 3, i2 = 4 });
            tempList1.Add(new tempClass1() { i1 = 5, i2 = 6 });

            tempList2.Add(new tempClass2() { i3 = 1, i4 = 2 });

            var result = from t1 in tempList1
                         join t2 in tempList2 on
                         new CompareClass { compare1 = t1.i1, compare2 = t1.i2 } equals
                         new CompareClass { compare1 = t2.i3, compare2 = t2.i4 }
                         select t1;
        }
        catch(Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }

I don't know what is different these codes. Please tell me some guide line or feedback.

  • You need to read [this](http://stackoverflow.com/questions/1936103/linq-equals-keyword-revisited-does-it-compare-values-and-references-to-objec) SO question. – Jeroen Heier Apr 29 '17 at 05:07

3 Answers3

0

Why the first snippet works? It is described in "Composite Key Join Example" paragraph of this article. [EDITED] As other guys described the rules of two dynamic objects comparing kick in here which boils down to be property based comparing.

Why does the second snippet not work? Because objects in this condition

new CompareClass { compare1 = t1.i1, compare2 = t1.i2 } equals
new CompareClass { compare1 = t2.i3, compare2 = t2.i4 }

does not make composite key described in there, thus they are treated as ordinary objects and get compared as ordinary objects as well, i.e. by reference. Since they get created "on the fly" right there in comparison those are two different objects on eact iteration; consequently, they will always be not equal to each other and such a query will always return an empty result.

There's a way to fix it though. Not sure it makes any sense for this particular situation but still technically it is possible. Just implement IComparable in CompareClass, and the second snippet will work as well.

Alexander Leonov
  • 4,694
  • 1
  • 17
  • 25
0

In first sample, you compare using a anonymous type. The comparison of anonymous type is made on properties and not on an instance.

https://msdn.microsoft.com/en-us/library/bb397696.aspx

Because the Equals and GetHashCode methods on anonymous types are defined in terms of the Equals and GetHashCode methods of the properties, two instances of the same anonymous type are equal only if all their properties are equal.

In second sample, the comparison is made in the instance of class.

For example:

var c1 = new CompareClass { compare1 = 1, compare2 = 1 };
var c2 = new CompareClass { compare1 = 1, compare2 = 1 };
var c3 = c1;

var notEqual = c1 == c2; //false
var equal = c1 == c3; //true

The instance of object c1 is the same of object c3. In c2 the values is same but instance is different.

0

The difference is that in first code you are comparing anonymous type objects, and in second - CompareClass instances.

    var c1 = new CompareClass() { compare1 = tempList1[0].i1, compare2 = tempList1[0].i2 };
    var c2 = new CompareClass() { compare1 = tempList2[0].i3, compare2 = tempList2[0].i4 };

    var c3 = new { compare1 = tempList1[0].i1, compare2 = tempList1[0].i2 };
    var c4 = new { compare1 = tempList2[0].i3, compare2 = tempList2[0].i4 };

Even though c1 and c2 properties are equal, but .GetHashCode() for c1 and c2 are different, because it is different instances.

    c1.GetHashCode() == c2.GetHashCode()

But anonymous types .GetHahCode() uses only its properties.

Because the Equals and GetHashCode methods on anonymous types are defined in terms of the Equals and GetHashCode methods of the properties, two instances of the same anonymous type are equal only if all their properties are equal.

So this will generate exact hash.

    c3.GetHashCode() == c4.GetHashCode()

If you want to compare class instances in LINQ statement, you need to override .GetHashCode() and .Equals() with your own implementation

    public override bool Equals(object obj)
    {
        if (obj.GetType() != typeof(CompareClass))
            return false;

        if (this.compare1 == ((CompareClass)obj).compare1 && this.compare2 == ((CompareClass)obj).compare2)
            return true ;
        else
            return false;
    }

    // oversimplified, this link is more appropriate
    // http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode
    public override int GetHashCode()
    {
        return (this.compare1.GetHashCode() + this.compare2.GetHashCode()) * 11 + 2;
    }
Mantas Čekanauskas
  • 2,218
  • 6
  • 23
  • 43