LINQ按自定义类型分组不起作用 [英] LINQ Grouping by custom type not working

查看:86
本文介绍了LINQ按自定义类型分组不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有通过HTTPWebRequest从Web服务接收到的数据.在使用NewtonSoft.Deserialize将其解析为自定义类型(具有公共字符串属性的简单类)之后,我想使用LINQ来处理此数据-更具体地说,我想对数据进行分组.

I have data I receive from a web service via HTTPWebRequest. After I parse it using NewtonSoft.Deserialize into a custom type (a simple class with public string properties), I want to manipulate this data using LINQ - more specifically, I want to group the data.

我的问题是,如果按单个字符串属性进行分组,分组会很好

My problem is that the grouping works fine if I group by a single string property

from x in myList
group x by x.myStr into grp
select grp;

由于我想按更多列进行分组,因此我将使用

Since I want to group by more columns, I am returning a custom type with

new MyType { a = ..., b = ... }

该群组无法正常工作.我认为原因一定是编译器不知道如何比较这些对象-因此,如果此类型实现IEqualityComparer<MyType>,它将解决该问题.

The group is however not working. I thought the reason must be the compiler does not know how to compare these objects - so if this type implements IEqualityComparer<MyType> it will solve it.

但是,它仍然没有进行相应的分组,它会创建几个具有完全相同的字符串值的键.

But no, it is still not grouping accordingly, and it creates several keys with the exact same string values.

我进行分组的自定义类型类似于

The custom type by which I am grouping is something like

public class MyType
{
    public string a;
    public string b;
    public string c;
}

关于我想念什么的任何想法吗?

Any ideas of what am I missing?

这是上述情况的具体示例:

Here's a concrete example of the scenario described above:

//The type that models the data returned from the web service
public class MyClass
{
    public string a { get; set; }

    public string b { get; set; }

    public string c { get; set; }

    public DateTime d { get; set; }

    public DateTime e { get; set; }
}

// the type by which I want to group my data
public class MyGroup : IEquatable<MyGroup>, IEqualityComparer<MyGroup>
{
    public string f1 { get; set; }

    public DateTime d1 { get; set; }

    public DateTime d2 { get; set; }

    public bool Equals(MyGroup other)
    {
        return string.Compare(this.f1, other.f1) == 0;
    }

    public bool Equals(MyGroup x, MyGroup y)
    {
        return string.Compare(x.f1, y.f1) == 0;
    }

    public int GetHashCode(MyGroup obj)
    {
        return obj.GetHashCode();
    }
}    
    List<MyClass> l = new List<MyClass>();
    l.Add(new MyClass { a = "aaa", b = "bbb", c = "ccc", d = DateTime.ParseExact("20081405", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140101", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });
    l.Add(new MyClass { a = "aaaa", b = "bbb", c = "ccc", d = DateTime.ParseExact("20090105", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140201", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });
    l.Add(new MyClass { a = "aa", b = "bbbb", c = "cccc", d = DateTime.ParseExact("20081405", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140201", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });
    l.Add(new MyClass { a = "aaa", b = "bbbbb", c = "ccc", d = DateTime.ParseExact("20121111", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140101", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });
    l.Add(new MyClass { a = "aaaaa", b = "bbb", c = "ccc", d = DateTime.ParseExact("20081405", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140101", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });
    l.Add(new MyClass { a = "aaaa", b = "bbbbb", c = "ccc", d = DateTime.ParseExact("20121111", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140101", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });
    l.Add(new MyClass { a = "aaaa", b = "bbbb", c = "cccccc", d = DateTime.ParseExact("20081405", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140201", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });
    l.Add(new MyClass { a = "aaaaa", b = "bbb", c = "cccc", d = DateTime.ParseExact("20090105", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140301", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });
    l.Add(new MyClass { a = "aaa", b = "bbb", c = "cccc", d = DateTime.ParseExact("20081405", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140201", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });

    //The following does not really group
    //IEnumerable<IGrouping<MyGroup, MyClass>> r = from x in l
    IEnumerable<IGrouping<string, MyClass>> r = from x in l
                                                //group x by new MyGroup { f1 = x.a /*, d1 = x.d, d2 = x.e*/ } into grp
                                                orderby x.a
                                                group x by x.a into grp
                                                select grp;

    //foreach (IGrouping<MyGroup, MyClass> g in r)
    foreach (IGrouping<string, MyClass> g in r)
    {
        //Console.WriteLine(g.Key.f1);

        Console.WriteLine(g.Key);
    }

推荐答案

我认为原因一定是编译器不知道如何比较这些对象-因此,如果此类型实现IEqualityComparer<MyType>,它将解决该问题.

I thought the reason must be the compiler does not know how to compare these objects - so if this type implements IEqualityComparer<MyType> it will solve it.

实际上,要在Linq函数中使用自定义相等"检查,您需要实现.

Actually, to use a custom "equality" check in Linq functions you need to implement IEquatable<T>. IEquatable<T> is used to compare an instance of an object with another object of the same type - while IEqualityProvider<T> is meant to be implemented by an external class to compare two arbitrary Ts (and/or to have multiple methods of determining "equality").

请注意,您还应该实现Object.EqualsObject.GetHashCode-IEquatable<T>只允许您以类型安全的方式进行比较.

Note that you should also implement Object.Equals and Object.GetHashCode - IEquatable<T> just allows you to compare in a type-safe manner.

为什么需要覆盖ObjectEqualsGetHashCode?

要确保用于比较两个对象的 any 方法(Object.Equals(object),静态Object.Equals(object, object等)是一致的.而且,每当您覆盖Equals时,也应该覆盖GetHashCode以确保可以将对象正确存储在基于哈希的集合中,例如DictionaryHashSet.

To ensure that any method (Object.Equals(object), the static Object.Equals(object, object, etc.) used to compare two objects is consistent. And any time you override Equals, you should also override GetHashCode to ensure that objects can be properly stored in a hash-based collection like a Dictionary or HashSet.

IEquitable仅以类型安全的方式进行比较是什么意思?

What does it mean IEquitable only compares in a type-safe manner?

使用IEquatable<T>时,保证要与之比较的对象T(或T的子类型),而对于Object.Equals,则不不知道另一个对象的类型,必须先检查它的类型.

When using IEquatable<T>, the object you're comparing to is guaranteed to be a T (or a subtype of T), whereas with Object.Equals, you don't know the type of the other object and must check it's type first.

例如:

// IEquatable<T>.Equals()
public bool Equals(MyGroup other)
{
    return string.Compare(this.f1, other.f1) == 0;
}

// Object.Equals()
public bool Equals(object other)
{
    // need to check the type of the passed in object
    MyGroup grp = other as MyGroup;

    // other is not a MyGroup
    if(grp == null return false);        

    return string.Compare(this.f1, grp.f1) == 0;

    // you could also use
    //    return this.Equals(grp);
    // as a shortcut to reuse the same "equality" logic
}

这篇关于LINQ按自定义类型分组不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆