Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does using anonymous type work and using an explicit type not in a GroupBy?

I have a problem where I want a group type to be strongly typed but if I do it doesn't group correctly. See the code below...

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication35
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Foo> foos = new List<Foo>();
            foos.Add(new Foo() { Key = "Test" });
            foos.Add(new Foo() { Key = "Test" });
            foos.Add(new Foo() { Key = "Test" });

            var groups = foos.GroupBy<Foo, dynamic>(entry => new
            {
                GroupKey = entry.Key
            });

            Console.WriteLine(groups.Count());

            groups = foos.GroupBy<Foo, dynamic>(entry => new GroupingKey()
            {
                GroupKey = entry.Key
            });

            Console.WriteLine(groups.Count());

        }

        public class Foo
        {
            public string Key { get; set; }
        }

        public class GroupingKey
        {
            public string GroupKey { get; set; }
        }
    } 
}

The output:

1
3
Press any key to continue . . .

I would expect the result to be the same regardless of using an explicit type nor not i.e. should only be one group with 3 items not 3 groups with 1 item. What is going on here?

Update I added an IEqualityComparer and it works now! See below:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication35
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Foo> foos = new List<Foo>();
            foos.Add(new Foo() { Key = "Test" });
            foos.Add(new Foo() { Key = "Test" });
            foos.Add(new Foo() { Key = "Test" });

            var groups = foos.GroupBy<Foo, dynamic>(entry => new //GroupingKey()
            {
                GroupKey = entry.Key
            });

            Console.WriteLine(groups.Count());

            groups = foos.GroupBy<Foo, GroupingKey>(entry => new GroupingKey()
            {
                GroupKey = entry.Key
            }, new GroupingKeyEqualityComparer());

            Console.WriteLine(groups.Count());

        }

        public class Foo
        {
            public string Key { get; set; }
        }

        public class GroupingKey
        {
            public string GroupKey { get; set; }              
        }

        public class GroupingKeyEqualityComparer : IEqualityComparer<GroupingKey>
        {
            #region IEqualityComparer<GroupingKey> Members

            public bool Equals(GroupingKey x, GroupingKey y)
            {
                return x.GroupKey == y.GroupKey;
            }

            public int GetHashCode(GroupingKey obj)
            {
                return obj.GroupKey.GetHashCode();
            }

            #endregion
        }
    } 
}

Output:

1
1
Press any key to continue . . .

This pretty much confirms the answer given by JaredPar!

like image 450
Jim Avatar asked Nov 04 '11 22:11

Jim


1 Answers

In the first version you are creating an anonymous type with a single property named GroupKey. Anonymous types in C# use structural equality so the equality of the values comes down to the equality of the keys. This causes them to be properly grouped together.

In the second case you are using a custom type named GroupingKey. It appears this uses the default or referential equality. Hence none of the instances are considered equal and hence they get put into different groups.

like image 157
JaredPar Avatar answered Oct 30 '22 00:10

JaredPar