.NET Tips and Tricks

Blog archive

Comparing Anonymous Objects in C# and VB

Trust me: There's actually a tip at the end of this column, but it's going to take me awhile to get there. Be patient -- I do have a point and I am getting to it.

When it comes to comparing objects, what .NET considers the same isn't what normal human beings consider the same. Consider this code that creates two Customer objects, both representing Customer A123:

Dim cust1 As New Customer("A123")
Dim cust2 as Customer
cust2 = cust1

In VB, if I try to compare the two objects using this syntax, I get a compile time error:

If cust1 = cust2 Then

I can, however, use this syntax and the result, unsurprisingly, is True:

If cust1.Equals(cust2) Then

The test is true, however, only because the variable cust1 is pointing at the same object as the variable cust2. The .NET documentation refers to this as "reference equality." If, however, I create two separate objects, as this code does, and compare cust1 and cust2, the test returns false:

Dim cust1 As New Customer("A123")
Dim cust2 As New Customer("A123")
If cust1.Equals(cust2) Then

The test fails because the cust1 variable is pointing at a different object than cust2, even though the two objects, presumably, represent exactly the same customer entity and have identical values in all of their properties.

We, as human beings, might consider these objects to be "the same" because the objects represent the same data -- that's called "value equality" -- but we're not .NET. C# gives the same results as VB, but is willing to accept the x == y syntax.

Anonymous and Different
.NET uses object equality for most objects, but there are exceptions. For scalar variables and strings, for instance, .NET uses value equality. As a result, this test is true:

Dim num1 As Integer = 2
Dim num2 As Integer = 2
If num1 = num2 Then

Things are also different if you use anonymous objects -- but only if you're using C#. In C#, you might create two anonymous objects like this:

var x = new {  id = "A123", 
    CompanyName = "PH&V Information Services" };
var y = new {  id = "A123", 
    CompanyName = "PH&V Information Services" };

In C#, this test is false:

if (x == y)

But this test is true:

if (x.Equals(y))

For anonymous objects and with the Equals method, C# stops using reference equality and starts using value equality: C# compares the value of properties with identical signatures (names and types). But wait! It gets worse. In VB, the anonymous objects look like this:

Dim x = New With {.id = "A123", 
    .CompanyName = "PH&V Information Services"}
Dim y = New With {.id = "A123", 
    .CompanyName = "PH&V Information Services"}

The x = y syntax is still an error but this test, which looks just like the C# test, gives the same answer as with "un-anonymous objects" and remains false:

If x.Equals(y) Then

So, unlike C#, VB continues to use reference equality with anonymous objects.

Listen: It's not my fault.

The Tip (Finally)
This is, of course, an accident waiting to happen. The good news is that you can fix this difference in behavior (and, by "fix", I mean: get the almost identical behavior almost everywhere). As a bonus, it simplifies your VB code when you do want to compare two anonymous objects: Just use the Key keyword to identify to VB properties to be used when comparing anonymous objects. Using Key causes VB to switch to using value equality when comparing anonymous objects, just like C#.

This example flags just the id property in my anonymous object as providing the value to be used when comparing this anonymous object with another anonymous object:

Dim x = New With {Key .id = "A123", CompanyName …
Dim y = New With {Key .id = "A123", CompanyName …

The x = y syntax is still a syntax error, but x.Equals(y) now returns the same result as the equivalent C# test: True. And, actually, VB's approach is more flexible than C#'s, as you can choose what counts as equality: all of the properties on the object or some arbitrary collection. One caveat: If you're comparing anonymous objects in either C# or VB, they must have identical sets of properties or the comparison automatically fails.

Of course, in C# x == y still returns a different result than x.Equals(y) for anonymous objects.

Hey: There's only so much I can do.

Posted by Peter Vogel on 06/25/2012 at 1:16 PM


comments powered by Disqus

Reader Comments:

Wed, Oct 3, 2012

The equality check using the = operator should check to see if the values are the same. In most programming languages this is true. Having it different in C# and object programming languages is just plain nuts. If one wants to know if the values in any object are the same or not, regardless of where they point, then they would use... if (a==b) regardless of the number of properties. If one wants to see if the object points to another location in memory then the following is more logical... if (a.pointer == b.pointer)

Tue, Jul 10, 2012 Peter Vogel Canada

Eric: I was waiting for someone to point out the Is syntax for comparing objects in VB. And you're right--thanks for rolling that into the discussion. But, I think, my point still stands: the = blows up in one language and not in the other; in one language, the = and Equals give different results; the result of Equals in one language is different from the result in the other. Not what you might expect from a naive point of view and using the Key attribute (to make part of the VB anonymous object invariant as an reader poster pointed out) helps reduce--though not eliminate--the discrepancies.

Mon, Jul 9, 2012 Eric Netherlands

In the first paragraph you are missing out part of the VB-syntax. Reference types should be compared using 'Is'. So the first If-statement should read: If cust1 Is Cust2 Then ... and the compile time error will go away (and the result will be 'True').

Sun, Jul 8, 2012 Keith Ward

Just an FYI, that the code in the first snippet is fixed, and should compile correctly now.

Thu, Jul 5, 2012 Richard

In your first example, the assignment is the wrong way round:

cust1 = cust2

You'll either get a compiler error for using a variable which hasn't been assigned, or cust1 will be a null reference, and you'll get a NullReferenceException when you try to call "cust1.Equals(cust2)".

Thu, Jul 5, 2012 Burton H Roberts USA

This turned out to be a very helpful tip for me at just the right time. Thanks.

Wed, Jun 27, 2012 Peter Vogel Canada

Jonathan: I thought about getting into the whole mutable/immutable thing to provide a reason for why the results are different--but this tip was already way too long. So I fell back on my usual approach: If you want this result then code this; if you code this then you'll get this result. But I still remember losing two hours of my life the first time I tried to update a property in a C# anonymous object...I simply didn't believe the error message I was getting and kept trying to figure out why my properties were read-only. But that's another tip or, because of you, now an extension to this tip. Thanks!

Mon, Jun 25, 2012 Jonathan Allen

You are missing a hige part of the picture. In C#, anonymous types are always immutable. In VB, only key properties are read-only, the rest default to read-write. So here is a recap of the rules: 1. Hash codes can never change, as that would break collections like HashSet and Dictionary. 2. If A and B are equal, the hash codes of A and B must be equal. Again, this is needed for collections like HashSet and Dictionary. 3. Because of [1], properties that are mutable cannot be part of the hash code calculation. 4. Because of [2] and [3], properties that are mutable cannot be part of equals. And that's why VB doesn't override equals unless you mark at least one property with 'key'.

Add Your Comments Now:

Your Name:(optional)
Your Email:(optional)
Your Location:(optional)
Comment:
Please type the letters/numbers you see above

.NET Insight

Sign up for our newsletter.

I agree to this site's Privacy Policy.