using System;
using System.Collections.Generic;
using System.Linq;
namespace Swan
{
///
/// Provides a way for types that override
/// to correctly generate a hash code from the actual status of an instance.
/// CompositeHashCode must be used ONLY as a helper when implementing
/// IEquatable<T> in a STANDARD way, i.e. when:
///
/// - two instances having the same hash code are actually
/// interchangeable, i.e. they represent exactly the same object (for instance,
/// they should not coexist in a
/// SortedSet);
/// - GetHashCode and
/// Equals are BOTH overridden, and the Equals
/// override either calls to the IEquatable<T>.Equals
/// (recommended) or performs exactly the same equality checks;
/// - only "standard" equality checks are performed, i.e. by means of the
/// == operator, IEquatable<T> interfaces, and
/// the Equals method (for instance, this excludes case-insensitive
/// and/or culture-dependent string comparisons);
///
/// - the hash code is constructed (via Using calls) from the very same
/// fields and / or properties that are checked for equality.
///
/// For hashing to work correctly, all fields and/or properties involved in hashing must either
/// be immutable, or at least not change while an object is referenced in a hashtable.
/// This does not refer just to System.Collections.Hashtable; the .NET
/// Framework makes a fairly extensive use of hashing, for example in
/// SortedSet<T>
/// and in various parts of LINQ. As a thumb rule, an object must stay the same during the execution of a
/// LINQ query on an IEnumerable
/// in which it is contained, as well as all the time it is referenced in a Hashtable or SortedSet.
///
///
/// The following code constitutes a minimal use case for CompositeHashCode, as well
/// as a reference for standard IEquatable<T> implementation.
/// Notice that all relevant properties are immutable; this is not, as stated in the summary,
/// an absolute requirement, but it surely helps and should be done every time it makes sense.
/// using System;
/// using Swan;
///
/// namespace Example
/// {
/// public class Person : IEquatable<Person>
/// {
/// public string Name { get; private set; }
///
/// public int Age { get; private set; }
///
/// public Person(string name, int age)
/// {
/// Name = name;
/// Age = age;
/// }
///
/// public override int GetHashCode() => CompositeHashCode.Using(Name, Age);
///
/// public override bool Equals(object obj) => obj is Person other && Equals(other);
///
/// public bool Equals(Person other)
/// => other != null
/// && other.Name == Name
/// && other.Age == Age;
/// }
/// }
///
public static class CompositeHashCode
{
#region Private constants
private const int InitialSeed = 17;
private const int Multiplier = 29;
#endregion
#region Public API
///
/// Computes a hash code, taking into consideration the values of the specified
/// fields and/oror properties as part of an object's state. See the
/// example.
///
/// The values of the fields and/or properties.
/// The computed has code.
public static int Using(params object[] fields)
{
unchecked
{
return fields.Where(f => !(f is null))
.Aggregate(InitialSeed, (current, field) => (Multiplier * current) + field.GetHashCode());
}
}
#endregion
}
}