C# 复杂对象上的分组依据(例如列表<;T>;)

C# 复杂对象上的分组依据(例如列表<;T>;),c#,linq,comparison,grouping,C#,Linq,Comparison,Grouping,使用GroupBy()和Count()>1我试图在列表中找到我的类的重复实例 该类如下所示: public class SampleObject { public string Id; public IEnumerable<string> Events; } var eventList1 = new List<string>() { "ExampleEvent" }; var eventList2 = new List<string>() {

使用
GroupBy()
Count()>1
我试图在列表中找到我的类的重复实例

该类如下所示:

public class SampleObject
{
    public string Id;
    public IEnumerable<string> Events;
}
var eventList1 = new List<string>() { "ExampleEvent" };
var eventList2 = new List<string>() { "ExampleEvent" };

var items = new List<SampleObject>()
{
    new SampleObject() { Id = "Id", Events = eventList1 },
    new SampleObject() { Id = "Id", Events = eventList2 }
};

var duplicates = items.GroupBy(x => x, new SampleObjectComparer())
                 .Where(g => g.Count() > 1)
                 .Select(g => g.Key)
                 .ToList();

Console.WriteLine(duplicates.Count);
公共类SampleObject
{
公共字符串Id;
公共事件;
}
这就是我实例化和分组列表的方式:

public class Program
{
    private static void Main(string[] args)
    {
        var items = new List<SampleObject>()
        {
            new SampleObject() { Id = "Id", Events = new List<string>() { "ExampleEvent" } },
            new SampleObject() { Id = "Id", Events = new List<string>() { "ExampleEvent" } }
        };

        var duplicates = items.GroupBy(x => new { Token = x.Id, x.Events })
                         .Where(g => g.Count() > 1)
                         .Select(g => g.Key)
                         .ToList();
    }
}
公共类程序
{
私有静态void Main(字符串[]args)
{
var items=新列表()
{
new SampleObject(){Id=“Id”,Events=new List(){“ExampleEvent”},
new SampleObject(){Id=“Id”,Events=new List(){“ExampleEvent”}
};
var duplicates=items.GroupBy(x=>new{Token=x.Id,x.Events})
.Where(g=>g.Count()>1)
.选择(g=>g.Key)
.ToList();
}
}

重复的
不包含任何项目。如何使分组工作?

要使对象与许多LINQ运算符(如
GroupBy
Distinct
)一起工作,必须实现
GetHashCode
&
Equals
,或者必须提供自定义比较器

在您的情况下,如果将属性作为列表,您可能需要一个比较器,除非您将该列表设置为只读

试试这个比较器:

public class SampleObjectComparer : IEqualityComparer<SampleObject>
{
    public bool Equals(SampleObject x, SampleObject y)
    {
        return x.Id == y.Id && x.Events.SequenceEqual(y.Events);
    }

    public int GetHashCode(SampleObject x)
    {
        return x.Id.GetHashCode() ^ x.Events.Aggregate(0, (a, y) => a ^ y.GetHashCode());
    }
}
公共类SampleObjectComparer:IEqualityComparer
{
公共布尔等于(采样对象x、采样对象y)
{
返回x.Id==y.Id&&x.Events.SequenceEqual(y.Events);
}
公共int GetHashCode(SampleObject x)
{
返回x.Id.GetHashCode()^x.Events.Aggregate(0,(a,y)=>a^y.GetHashCode());
}
}
现在,该代码起作用了:

    var items = new List<SampleObject>()
    {
        new SampleObject() { Id = "Id", Events = new List<string>() { "ExampleEvent"} },
        new SampleObject() { Id = "Id", Events = new List<string>() { "ExampleEvent" } }
    };

    var comparer = new SampleObjectComparer();

    var duplicates = items.GroupBy(x => x, comparer)
                     .Where(g => g.Count() > 1)
                     .Select(g => g.Key)
                     .ToList();
var items=新列表()
{
new SampleObject(){Id=“Id”,Events=new List(){“ExampleEvent”},
new SampleObject(){Id=“Id”,Events=new List(){“ExampleEvent”}
};
var comparer=新的SampleObjectComparer();
var duplicates=items.GroupBy(x=>x,比较器)
.Where(g=>g.Count()>1)
.选择(g=>g.Key)
.ToList();
列表
没有被覆盖的
等于
+
GetHashCode
,这就是为什么您的
分组依据
不能按预期工作的原因。当
GroupBy
必须比较两个列表
对象时,匿名类型的两个属性之一引用列表。使用referenceequals
仅检查两者是否为相同的引用,而不检查两者是否都包含示例元素

您可以提供一个自定义的
IEqualityComparer

GroupBy()
将执行默认比较,导致它发现您的列表不相等

请参阅以下代码:

var eventList1 = new List<string>() { "ExampleEvent" };
var eventList2 = new List<string>() { "ExampleEvent" };

Console.WriteLine(eventList1.GetHashCode());
Console.WriteLine(eventList2.GetHashCode());
Console.WriteLine(eventList1.Equals(eventList2));
因此,它们被认为是不平等的,因此没有分组

您需要提供一个自定义比较器,用于比较对象的相关属性。请注意,如前所示,
List.GetHashCode()
不能正确表示列表中的项目

您可以这样做(从和):

公共类SampleObjectComparer:IEqualityComparer
{
公共布尔等于(采样对象a、采样对象b)
{
返回a.Id==b.Id
&&a.事件顺序相等(b.事件);
}
公共int GetHashCode(SampleObject a)
{
int hash=17;
hash=hash*23+a.Id.GetHashCode();
foreach(a.Events中的var evt)
{
hash=hash*31+evt.GetHashCode();
}           
返回散列;
}
}
然后像这样使用它:

public class SampleObject
{
    public string Id;
    public IEnumerable<string> Events;
}
var eventList1 = new List<string>() { "ExampleEvent" };
var eventList2 = new List<string>() { "ExampleEvent" };

var items = new List<SampleObject>()
{
    new SampleObject() { Id = "Id", Events = eventList1 },
    new SampleObject() { Id = "Id", Events = eventList2 }
};

var duplicates = items.GroupBy(x => x, new SampleObjectComparer())
                 .Where(g => g.Count() > 1)
                 .Select(g => g.Key)
                 .ToList();

Console.WriteLine(duplicates.Count);
var eventList1=new List(){“examplevent”};
var eventList2=新列表(){“ExampleEvent”};
var items=新列表()
{
新建SampleObject(){Id=“Id”,Events=eventList1},
新建SampleObject(){Id=“Id”,Events=eventList2}
};
var duplicates=items.GroupBy(x=>x,新的SampleObjectComparer())
.Where(g=>g.Count()>1)
.选择(g=>g.Key)
.ToList();
Console.WriteLine(副本数);

默认情况下,列表不会根据其项的值进行比较。@SergeyBerezovskiy-这不是问题所在。问题是缺少
GetHashCode
Equals
覆盖。@Enigmativity
new{Token=x.Id,x.Events}
与覆盖
SampleObject
的Equals和GetHashCode无关。这里的问题完全是由
x.Events
comparison引起的虽然我不会使用
作为分隔符,@Farhad(
string.Join
)提出的解决方案是经典的,而且通常“足够好”。我将使用
'\0'
作为分隔符。另一个“东西”。。。然后我将使用
.Select(g=>g.First())
检索重复的项目。@SergeyBerezovskiy-是的,很公平。我不得不求助于使用自定义比较器与原始对象进行比较。嗨,蒂姆。非常好的例子和解释。但是,当我尝试实现此功能时,使用以下行:var groupedOrderList=orders.OrderListD.GroupBy(u=>u.Services[0].FeatureList,new FeatureAttributeComparer()).Select(grp=>grp.First()).ToList();我在GroupBy子句下得到了一个标记为:无法从用法推断类型参数的错误。尝试显式指定类型参数。你知道这个错误是什么吗?
public class SampleObjectComparer : IEqualityComparer<SampleObject>
{
    public bool Equals(SampleObject a, SampleObject b)
    {
        return a.Id == b.Id 
            && a.Events.SequenceEqual(b.Events);
    }

    public int GetHashCode(SampleObject a)
    {
        int hash = 17;

        hash = hash * 23 + a.Id.GetHashCode();

        foreach (var evt in a.Events)
        {
            hash = hash * 31 + evt.GetHashCode();
        }           

        return hash;
    }
}
var eventList1 = new List<string>() { "ExampleEvent" };
var eventList2 = new List<string>() { "ExampleEvent" };

var items = new List<SampleObject>()
{
    new SampleObject() { Id = "Id", Events = eventList1 },
    new SampleObject() { Id = "Id", Events = eventList2 }
};

var duplicates = items.GroupBy(x => x, new SampleObjectComparer())
                 .Where(g => g.Count() > 1)
                 .Select(g => g.Key)
                 .ToList();

Console.WriteLine(duplicates.Count);