Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected behaviour when comparing GUIDs in .NET

I have attempted to create an extension method that looks like this...

public static IEnumerable<T> Distinct<T>(this IEnumerable<T> value, IEnumerable<T> compareTo, Func<T, object> compareFieldPredicate)
{
    return value.Where(o => !compareTo.Exists(p => compareFieldPredicate.Invoke(p) == compareFieldPredicate.Invoke(o)));
}

The idea is that I would be able to do something like this...

IEnumerable<MyCollection> distinctValues = MyCollection.Distinct(MyOtherCollection, o => o.ID); //Note that o.ID is a guid

Now at this point I would have expected to have only my distinct items returned to me but what I found is that this was never the case.

Upon further research breaking down this method using the following code.

Guid guid1 = Guid.NewGuid();
Guid guid2 = new Guid(guid1.ToString());

Func<MyObject, object> myFunction = o => o.ID;
Func<MyObject, object> myFunction1 = o => o.ID;

bool result = myFunction(MyObject) == myFunction1(MyObject);
//result = false

I have found that infact even if the Guids are the same the comparison will always return false.

What is the cause of this?

like image 202
Maxim Gershkovich Avatar asked Dec 20 '22 19:12

Maxim Gershkovich


2 Answers

Your problem is that you're boxing the Guids into Objects before you compare them. Consider this code:

Guid g1 = Guid.NewGuid();
var g2 = g1;

Console.WriteLine(g1 == g2);

object o1 = g1;
object o2 = g2;

Console.WriteLine(o1 == o2);

That actually outputs:

true
false

Since "o1" and "o2", while equal to the same Guid, are not the same object.

If you truly want your "Distinct" extension method to not be tied to a specific type (like Guid), you can do this:

public static IEnumerable<TItem> Distinct<TItem, TProp>(this IEnumerable<TItem> value, IEnumerable<TItem> compareTo, Func<TItem, TProp> compareFieldPredicate)
    where TProp : IEquatable<TProp>
{
    return value.Where(o => !compareTo.Any(p => compareFieldPredicate(p).Equals(compareFieldPredicate(o))));
} 
like image 99
Matt Hamilton Avatar answered Jan 05 '23 19:01

Matt Hamilton


bool result = (guid1==guid2); //result -> true

You can try to change return type Object to GUID in myfunction and myfunction1

Func<MyObject, Guid> myFunction = o => o.ID;
Func<MyObject, Guid> myFunction1 = o => o.ID;

Otherwise, the return value (true) is boxed to Object, and Reference equality is checked, which is false.

like image 20
Tilak Avatar answered Jan 05 '23 21:01

Tilak