【问题标题】:Why this multi linq join code does not work?为什么这个多 linq 连接代码不起作用?
【发布时间】:2017-04-29 11:34:00
【问题描述】:

我在 c#-LINQ 中做了一个简单的多连接代码。

但有些问题是这样发生的。

示例 1) 做得很好。结果是 [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());
        }
    }

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

我不知道这些代码有什么不同。请告诉我一些指导方针或反馈。

【问题讨论】:

  • 你需要阅读this SO问题。

标签: c# linq join


【解决方案1】:

在第一个示例中,您使用匿名类型进行比较。匿名类型的比较是在属性而不是实例上进行的。

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

因为匿名类型的 Equals 和 GetHashCode 方法是 根据 Equals 和 GetHashCode 方法定义 属性,相同匿名类型的两个实例仅当 它们的所有属性都是相等的。

在第二个示例中,比较是在类的实例中进行的。

例如:

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

对象 c1 的实例与对象 c3 相同。在 c2 中,值相同,但实例不同。

【讨论】:

    【解决方案2】:

    为什么第一个 sn-p 有效?它在this 文章的“复合键连接示例”段落中进行了描述。 [已编辑] 正如其他人描述的两个动态对象的比较规则在这里开始,归结为基于属性的比较。

    为什么第二个 sn-p 不起作用?因为在这种情况下的对象

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

    不在那里描述复合键,因此它们被视为普通对象并作为普通对象进行比较,即通过引用。由于它们是“即时”创建的,相比之下,它们是 eact 迭代中的两个不同对象;因此,它们将始终不相等,并且这样的查询将始终返回空结果。

    不过有办法解决它。不确定这对这种特殊情况是否有意义,但在技术上仍然是可能的。只需在 CompareClass 中实现 IComparable,第二个 sn-p 也可以。

    【讨论】:

      【解决方案3】:

      不同之处在于,在第一个代码中您要比较匿名类型对象,而在第二个代码中 - CompareClass 实例。

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

      虽然c1c2 属性是相等的,但是.GetHashCode() 对于c1c2 是不同的,因为它是不同的实例。

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

      但是匿名类型 .GetHahCode() 只使用它的属性。

      因为匿名类型的 Equals 和 GetHashCode 方法是根据属性的 Equals 和 GetHashCode 方法定义的,所以相同匿名类型的两个实例只有在它们的所有属性都相等时才相等。

      所以这将生成精确的哈希。

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

      如果要比较 LINQ 语句中的类实例,需要用自己的实现覆盖 .GetHashCode().Equals()

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

      【讨论】:

        猜你喜欢
        • 2018-11-05
        • 2012-02-22
        • 2023-03-03
        • 2017-10-02
        • 2016-07-10
        • 2010-12-14
        • 2017-04-09
        • 2013-01-30
        • 2014-05-23
        相关资源
        最近更新 更多