无法使用LINQ从列表中获取不同的项目

时间:2017-06-22 17:26:02

标签: c# linq anonymous-class

我在C#中有以下课程,我试图找到一个独特的项目列表。 该清单有24个要素。

public enum DbObjectType
{
    Unknown,
    Procedure,
    Function,
    View
}

public class DbObject
{
    public string DatabaseName { get; set; }
    public string SchemaName { get; set; }
    public string ObjectName { get; set; }
    public DbObjectType ObjectType { get; set; }
}

我有两种方法并希望得到相同的结果,但我不会。

第一个表达式返回相同的列表(包括重复项)

var lst1 = from c in DependantObject
          group c by new DbObject
          {
              DatabaseName = c.DatabaseName,
              SchemaName = c.SchemaName,
              ObjectName = c.ObjectName,
              ObjectType = c.ObjectType
          } into grp
          select grp.First();
  

lst1将有24个项目。

但是这个会返回所需的结果。

var lst2 = from c in DependantObject
          group c by new 
          {
              DatabaseName = c.DatabaseName,
              SchemaName = c.SchemaName,
              ObjectName = c.ObjectName,
              ObjectType = c.ObjectType
          } into grp
          select grp.First();
  

lst2将有10个项目。

唯一的区别是第二个表达式是匿名的,但第一个表达式是键入的。

我有兴趣了解这种行为。

谢谢!

我相信我的问题与上述问题不重复,因为: 我在这里问的不是如何获得不同的清单。我问为什么Typed和Anonymous数据会返回不同的结果。

1 个答案:

答案 0 :(得分:2)

Linq的Distinct()方法需要覆盖GetHashCodeEquals

C#的anoynmous类型(new { Name = value }语法)会创建覆盖这些方法的类,但您自己的DbObject类型却没有。

您也可以创建自定义IEqualityComparer类型。看看StructuralComparisons.StructuralEqualityComparer

选项1:

public class DbObject : IEquatable<DbObject> {

    public override Int32 GetHashCode() {

        // See https://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode

        unchecked
        {
            int hash = 17;
            hash = hash * 23 + this.DatabaseName.GetHashCode();
            hash = hash * 23 + this.SchemaName.GetHashCode();
            hash = hash * 23 + this.ObjectName.GetHashCode();
            hash = hash * 23 + this.ObjectType.GetHashCode();
            return hash;
        }
    }

    public override Boolean Equals(Object other) {

        return this.Equals( other as DbObject );    
    }

    public Boolean Equals(DbObject other) {

        if( other == null ) return false;
        return
            this.DatabaseName.Equals( other.DatabaseName ) &&
            this.SchemaName.Equals( other.SchemaName) &&
            this.ObjectName.Equals( other.ObjectName ) &&
            this.ObjectType.Equals( other.ObjectType);
    }
}

选项2:

class DbObjectComparer : IEqualityComparer {

    public Boolean Equals(DbObject x, DbObject y) {

        if( Object.ReferenceEquals( x, y ) ) return true;
        if( (x == null) != (y == null) ) return false;
        if( x == null && y == null ) return true;

         return
            x.DatabaseName.Equals( y.DatabaseName ) &&
            x.SchemaName.Equals( y.SchemaName) &&
            x.ObjectName.Equals( y.ObjectName ) &&
            x.ObjectType.Equals( y.ObjectType);
    }

    public override Int32 GetHashCode(DbObject obj) {

        unchecked
        {
            int hash = 17;
            // Suitable nullity checks etc, of course :)
            hash = hash * 23 + obj.DatabaseName.GetHashCode();
            hash = hash * 23 + obj.SchemaName.GetHashCode();
            hash = hash * 23 + obj.ObjectName.GetHashCode();
            hash = hash * 23 + obj.ObjectType.GetHashCode();
            return hash;
        }
    }
}

选项2用法:

var query = this.DependantObject
    .GroupBy( c => new DbObject() {
        DatabaseName = c.DatabaseName,
        SchemaName   = c.SchemaName,
        ObjectName   = c.ObjectName,
        ObjectType   = c.ObjectType
    } )
    .First();

使用GroupBy可能不是最理想的,您可以直接使用Linq Distinct

var query = this.DependantObject
    .Select( c => new DbObject() {
        DatabaseName = c.DatabaseName,
        SchemaName   = c.SchemaName,
        ObjectName   = c.ObjectName,
        ObjectType   = c.ObjectType
    } )
    .Distinct()
    .First();