无法使用 LINQ 从列表中获取不同的项目
Can't get distinct items from a list using LINQ
我在 C# 中关注 class,我正试图找到一个不同的项目列表。
该列表有 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; }
}
我有两种方法,希望得到相同的结果,但我没有。
第一个表达式returns me the same list (includes duplicates)
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 will have 24 items.
但是这个 returns 想要的结果。
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 will have 10 items.
唯一的区别是第二个表达式是匿名的,而第一个是键入的。
我很想了解这种行为。
谢谢!
我相信我的问题与上述问题不重复,因为:
我在这里问的不是如何获得不同的列表。我在问为什么类型化数据和匿名数据返回不同的结果。
Linq 的 Distinct()
方法需要覆盖 GetHashCode
和 Equals
。
C# 的异常类型(new { Name = value }
语法)创建的 类 会覆盖这些方法,但您自己的 DbObject
类型不会。
您也可以创建自定义 IEqualityComparer
类型。也看看StructuralComparisons.StructuralEqualityComparer
选项 1:
public class DbObject : IEquatable<DbObject> {
public override Int32 GetHashCode() {
// See
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();
我在 C# 中关注 class,我正试图找到一个不同的项目列表。 该列表有 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; }
}
我有两种方法,希望得到相同的结果,但我没有。
第一个表达式returns me the same list (includes duplicates)
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 will have 24 items.
但是这个 returns 想要的结果。
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 will have 10 items.
唯一的区别是第二个表达式是匿名的,而第一个是键入的。
我很想了解这种行为。
谢谢!
我相信我的问题与上述问题不重复,因为: 我在这里问的不是如何获得不同的列表。我在问为什么类型化数据和匿名数据返回不同的结果。
Linq 的 Distinct()
方法需要覆盖 GetHashCode
和 Equals
。
C# 的异常类型(new { Name = value }
语法)创建的 类 会覆盖这些方法,但您自己的 DbObject
类型不会。
您也可以创建自定义 IEqualityComparer
类型。也看看StructuralComparisons.StructuralEqualityComparer
选项 1:
public class DbObject : IEquatable<DbObject> {
public override Int32 GetHashCode() {
// See
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();