I believe the following code should generate two instances of the same anonymous type, with properties in the same order, with the same names, types and values.
static void Main(string[] args)
{
var letterFreq1 = CountLetters("aabbbc");
var letterFreq2 = CountLetters("aabbbc");
if (letterFreq1.Equals(letterFreq2))
Console.WriteLine("Is anagram");
else
Console.WriteLine("Is not an anagram");
}
public static object CountLetters(string input) => input.ToCharArray()
.GroupBy(x => x)
.Select(x => new {Letter = x.Key, Count = x.Count()})
.OrderBy(x => x.Letter)
.ToList();
According to MS documentation:
If two or more anonymous object initializers in an assembly specify a sequence of properties that are in the same order and that have the same names and types, the compiler treats the objects as instances of the same type. They share the same compiler-generated type information.
and
Because the Equals and GetHashCode methods on anonymous types are defined in terms of the Equals and GetHashCode methods of the properties, two instances of the same anonymous type are equal only if all their properties are equal.
I interpret this as meaning I should get equality on letterFreq1 and letterFreq2, but this isn't occurring. Can anyone identify why the equality check fails please? I'm trying to avoid a manual approach to comparing property values.
This is a similar question but doesn't seen to help solve my issue.
Many thanks.
I believe the following code should generate two instances of the same anonymous type
No, it generates two instances of List<T>
, with the same content.
So when you execute this:
if (letterFreq1.Equals(letterFreq2))
you're invoking the .Equals
method on List<T>
objects, which does not override the method inherited from System.Object
, and thus do reference comparison.
You're right, however, in that the anonymous types would compare equal, and the two lists does in fact have the same content, but the list objects doesn't do content comparison by themselves, so they will compare as different.
If you were to coax the compiler into converting the two into the same type of collection, such as:
var letterFreq1 = CountLetters("aabbbc") as IEnumerable<object>;
var letterFreq2 = CountLetters("aabbbc") as IEnumerable<object>;
then you could compare their contents:
if (letterFreq1.SequenceEqual(letterFreq2))
but then you need to first know that they are collections, so depending on how general/generic your code is supposed to be, this may or may not be a solution for you.
My real advice, however, would be to avoid using anonymous types in this case. They're nice when used with local variables, but as you notice, when they escape the confines of a method they become very cumbersome to work with.
A better replacement would be a tuple:
void Main(string[] args)
{
var letterFreq1 = CountLetters("aabbbc");
var letterFreq2 = CountLetters("aabbbc");
if (letterFreq1.SequenceEqual(letterFreq2))
Console.WriteLine("Is anagram");
else
Console.WriteLine("Is not an anagram");
}
public static List<(char Letter, int Count)> CountLetters(string input)
=> input.ToCharArray()
.GroupBy(x => x)
.Select(x => (Letter: x.Key, Count : x.Count()))
.OrderBy(x => x.Letter)
.ToList();
An even better solution would be to create a named type for this, but again, depending on your situation this may or may not be a good solution for you.