using System.Collections.Generic; namespace Caliburn.MemoryManagement { // Compares objects of the given type or WeakKeyReferences to them // for equality based on the given comparer. Note that we can only // implement IEqualityComparer for T = object as there is no // other common base between T and WeakKeyReference. We need a // single comparer to handle both types because we don't want to // allocate a new weak reference for every lookup. internal sealed class WeakKeyComparer : IEqualityComparer where T : class { private readonly IEqualityComparer comparer; internal WeakKeyComparer(IEqualityComparer comparer) { if(comparer == null) comparer = EqualityComparer.Default; this.comparer = comparer; } #region IEqualityComparer Members public int GetHashCode(object obj) { WeakKeyReference weakKey = obj as WeakKeyReference; if(weakKey != null) return weakKey.HashCode; return comparer.GetHashCode((T) obj); } // Note: There are actually 9 cases to handle here. // // Let Wa = Alive Weak Reference // Let Wd = Dead Weak Reference // Let S = Strong Reference // // x | y | Equals(x,y) // ------------------------------------------------- // Wa | Wa | comparer.Equals(x.Target, y.Target) // Wa | Wd | false // Wa | S | comparer.Equals(x.Target, y) // Wd | Wa | false // Wd | Wd | x == y // Wd | S | false // S | Wa | comparer.Equals(x, y.Target) // S | Wd | false // S | S | comparer.Equals(x, y) // ------------------------------------------------- public new bool Equals(object x, object y) { bool xIsDead, yIsDead; T first = GetTarget(x, out xIsDead); T second = GetTarget(y, out yIsDead); if(xIsDead) return yIsDead ? x == y : false; if(yIsDead) return false; return comparer.Equals(first, second); } #endregion private static T GetTarget(object obj, out bool isDead) { WeakKeyReference wref = obj as WeakKeyReference; T target; if(wref != null) { target = wref.Target; isDead = !wref.IsAlive; } else { target = (T) obj; isDead = false; } return target; } } }