Search code examples
c#nhibernateilist

IList<T> Count property invokes GetHashCode() (using NHibernate)


I get a strange issue. As the code shown below, list contain a lot of items. The code list.Count == 0; sometimes will invoke GetHashCode function of the item in the list.

public static bool IsNullOrEmpty<T>(this IList<T> list)
{
    if (list == null)
        return true;

    return list.Count == 0;
}

class Item
{
    int id;
    int Version;

    public override int GetHashCode()
    {
          unchecked
          {
                 return (Id * 397) ^ Version;
          }
    }
}

I don't know why this could be happened?

Thanks a lot for any infomation.

The list sometime contain nothing, the Count of list is 0. The list contain NHibernate item. [NHibernate.Collection.Generic.PersistentGenericBag]


Solution

  • As Shad says in comments it has to do with the implementation of your IList<T>. I went and checked source code of PersistentGenericBag<T> and it looks like this:

    public class PersistentGenericBag<T> : PersistentBag, IList<T> { }
    

    And PersistentBag looks like this:

    public class PersistentBag : AbstractPersistentCollection, IList { }
    

    The Count property is defined in this class PersistentBag which looks like:

    public int Count
    {
        get { return ReadSize() ? CachedSize : bag.Count; }
    }
    

    where bag is a simple IList and CachedSize merely an int property. So everything has got to do with ReadSize which is defined in AbstractPersistentCollection which looks like:

    protected virtual bool ReadSize()
    {
        if (!initialized)
        {
            if (cachedSize != -1 && !HasQueuedOperations)
            {
                return true;
            }
            else
            {
                ThrowLazyInitializationExceptionIfNotConnected();
                // the below line it has to be.
                CollectionEntry entry = session.PersistenceContext.GetCollectionEntry(this); 
                ICollectionPersister persister = entry.LoadedPersister;
                if (persister.IsExtraLazy)
                {
                    if (HasQueuedOperations)
                    {
                        session.Flush();
                    }
                    cachedSize = persister.GetSize(entry.LoadedKey, session);
                    return true;
                }
            }
        }
        Read();
        return false;
    }
    

    The session variable is of type ISessionImplementor, so everything will depend on how it is implemented. The GetCollectionEntry(bag) should be a call to get the item from the bag (a bag is a collection structure which allows duplicate elements) which will have to perform some equality checks before retrieving it, which in turn will have to call GetHashCode of the item.

    I have no idea what they do with all this, but it has got to do with the above method.

    References:
    PersistentGenericBag
    PersistentBag
    AbstractPersistentCollection