whymatter
whymatter

Reputation: 775

.Net, C# - Boolean.Equal differs from == compare

We are using AutoMapper to map from IDataReader to List of Entities.

One Problem that i noticed while unit testing was the following. If we read an bool value from database (bit), AutoMapper does verry fine. But when we us FluentAssertions for UnitTesting there is a problem with the ShouldAllBeEquivalentTo function. It says True expected but True returned on the bool property of the entity.

So i tried to check the bool properties and noticed that expected == returnd works (returns true) but expected.Equals(returned) does not work (returns false)?!

I thought == and equals should be almost the same for bool type?

What could cause this strange behaviour?

Here some code:

using (var connection = new SqlConnection("server=someServer;user id=someUser;password=***;database=someDatabase;MultipleActiveResultSets=True"))
using (var command = connection.CreateCommand())
{
    connection.Open();

    var itemsBefore = new List<Item> { new Item { CheckDispo = true } };

    command.CommandText = "SELECT CheckDispo FROM Items WHERE ItemId = 1814";
    var itemsAfter = Mapper.DynamicMap<List<Item>>(command.ExecuteReader());

    var a = itemsAfter[0].CheckDispo.Equals(true); // false
    var b = itemsAfter[0].CheckDispo == true; // true
}

public class Item
{
    public bool CheckDispo { get; set; }
}

Upvotes: 0

Views: 1250

Answers (2)

whymatter
whymatter

Reputation: 775

Sorry for this late edit, but I have found what has gone wrong. We read the property out of our database and our 'boolean' is represented by -1 not 1. Automapper uses dynamically generated IL code to read from a datareader and because they do this, automapper is able to write -1 to the bool field. This results in a value of 0xff in out RAM. If we now look at the il code for the equals method then we understand wy .equal is not ==.

.method public hidebysig newslot virtual final 
        instance bool  Equals(bool obj) cil managed
{
  .custom instance void __DynamicallyInvokableAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       6 (0x6)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldind.u1
  IL_0002:  ldarg.1
  IL_0003:  ceq
  IL_0005:  ret
} // end of method Boolean::Equals

This loads an 1 and our value to the stack and compares both. Due to the fact that in our RAM the bool is -1 (0xFF) the comparison will return false.

See also this item on github.com for more information.

Upvotes: 0

Joseph Caruana
Joseph Caruana

Reputation: 2271

Both results should return true. I have tested your same scenario above and it does return true in both cases. Please see below screenshot.

enter image description here

The boolean class overrides the Equals method as follows:

 public override bool Equals(object obj)
        {
            if (!(obj as bool))
            {
                return false;
            }
            return this == (bool)obj;
        }

It also provides a separate overloaded method which accepts a boolean as a parameter. This just uses the equality operator. I think this was added because it provides better performance over the Equals method because it avoids the typecasting overhead.

public bool Equals(bool obj)
        {
            return this == obj;
        }

In your case since you are passing a boolean it should hit the above overloaded method and as you can see, it is just using the === operator.

Upvotes: 2

Related Questions