//****************************** // Written by Peter Golde // Copyright (c) 2004-2005, Wintellect // // Use and restribution of this code is subject to the license agreement // contained in the file "License.txt" accompanying this file. //****************************** using System; using System.Collections.Generic; namespace Wintellect.PowerCollections { /// /// Stores a triple of objects within a single struct. This struct is useful to use as the /// T of a collection, or as the TKey or TValue of a dictionary. /// [Serializable] public struct Triple : IComparable, IComparable> { /// /// Comparers for the first and second type that are used to compare /// values. /// private static IComparer firstComparer = Comparer.Default; private static IComparer secondComparer = Comparer.Default; private static IComparer thirdComparer = Comparer.Default; private static IEqualityComparer firstEqualityComparer = EqualityComparer.Default; private static IEqualityComparer secondEqualityComparer = EqualityComparer.Default; private static IEqualityComparer thirdEqualityComparer = EqualityComparer.Default; /// /// The first element of the triple. /// public TFirst First; /// /// The second element of the triple. /// public TSecond Second; /// /// The thrid element of the triple. /// public TThird Third; /// /// Creates a new triple with given elements. /// /// The first element of the triple. /// The second element of the triple. /// The third element of the triple. public Triple(TFirst first, TSecond second, TThird third) { this.First = first; this.Second = second; this.Third = third; } /// /// Determines if this triple is equal to another object. The triple is equal to another object /// if that object is a Triple, all element types are the same, and the all three elements /// compare equal using object.Equals. /// /// Object to compare for equality. /// True if the objects are equal. False if the objects are not equal. public override bool Equals(object obj) { if (obj != null && obj is Triple) { Triple other = (Triple)obj; return Equals(other); } else { return false; } } /// /// Determines if this triple is equal to another triple. Two triples are equal if the all three elements /// compare equal using IComparable<T>.Equals or object.Equals. /// /// Triple to compare with for equality. /// True if the triples are equal. False if the triples are not equal. public bool Equals(Triple other) { return firstEqualityComparer.Equals(First, other.First) && secondEqualityComparer.Equals(Second, other.Second) && thirdEqualityComparer.Equals(Third, other.Third); } /// /// Returns a hash code for the triple, suitable for use in a hash-table or other hashed collection. /// Two triples that compare equal (using Equals) will have the same hash code. The hash code for /// the triple is derived by combining the hash codes for each of the two elements of the triple. /// /// The hash code. public override int GetHashCode() { // Build the hash code from the hash codes of First and Second. int hashFirst = (First == null) ? 0x61E04917 : First.GetHashCode(); int hashSecond = (Second == null) ? 0x198ED6A3 : Second.GetHashCode(); int hashThird = (Third == null) ? 0x40FC1877 : Third.GetHashCode(); return hashFirst ^ hashSecond ^ hashThird; } /// /// Compares this triple to another triple of the some type. The triples are compared by using /// the IComparable<T> or IComparable interface on TFirst, TSecond, and TThird. The triples /// are compared by their first elements first, if their first elements are equal, then they /// are compared by their second elements. If their second elements are also equal, then they /// are compared by their third elements. /// If TFirst, TSecond, or TThird does not implement IComparable<T> or IComparable, then /// an NotSupportedException is thrown, because the triples cannot be compared. /// /// The triple to compare to. /// An integer indicating how this triple compares to . Less /// than zero indicates this triple is less than . Zero indicate this triple is /// equals to . Greater than zero indicates this triple is greater than /// . /// Either FirstSecond, TSecond, or TThird is not comparable /// via the IComparable<T> or IComparable interfaces. public int CompareTo(Triple other) { try { int firstCompare = firstComparer.Compare(First, other.First); if (firstCompare != 0) return firstCompare; int secondCompare = secondComparer.Compare(Second, other.Second); if (secondCompare != 0) return secondCompare; else return thirdComparer.Compare(Third, other.Third); } catch (ArgumentException) { // Determine which type caused the problem for a better error message. if (!typeof(IComparable).IsAssignableFrom(typeof(TFirst)) && !typeof(System.IComparable).IsAssignableFrom(typeof(TFirst))) { throw new NotSupportedException(string.Format(Strings.UncomparableType, typeof(TFirst).FullName)); } else if (!typeof(IComparable).IsAssignableFrom(typeof(TSecond)) && !typeof(System.IComparable).IsAssignableFrom(typeof(TSecond))) { throw new NotSupportedException(string.Format(Strings.UncomparableType, typeof(TSecond).FullName)); } else if (!typeof(IComparable).IsAssignableFrom(typeof(TThird)) && !typeof(System.IComparable).IsAssignableFrom(typeof(TThird))) { throw new NotSupportedException(string.Format(Strings.UncomparableType, typeof(TThird).FullName)); } else throw; // Hmmm. Unclear why we got the ArgumentException. } } /// /// Compares this triple to another triple of the some type. The triples are compared by using /// the IComparable<T> or IComparable interface on TFirst, TSecond, and TThird. The triples /// are compared by their first elements first, if their first elements are equal, then they /// are compared by their second elements. If their second elements are also equal, then they /// are compared by their third elements. /// If TFirst, TSecond, or TThird does not implement IComparable<T> or IComparable, then /// an NotSupportedException is thrown, because the triples cannot be compared. /// /// The triple to compare to. /// An integer indicating how this triple compares to . Less /// than zero indicates this triple is less than . Zero indicate this triple is /// equals to . Greater than zero indicates this triple is greater than /// . /// is not of the correct type. /// Either FirstSecond, TSecond, or TThird is not comparable /// via the IComparable<T> or IComparable interfaces. int IComparable.CompareTo(object obj) { if (obj is Triple) return CompareTo((Triple)obj); else throw new ArgumentException(Strings.BadComparandType, "obj"); } /// /// Returns a string representation of the triple. The string representation of the triple is /// of the form: /// First: {0}, Second: {1}, Third: {2} /// where {0} is the result of First.ToString(), {1} is the result of Second.ToString(), and /// {2} is the result of Third.ToString() (or "null" if they are null.) /// /// The string representation of the triple. public override string ToString() { return string.Format("First: {0}, Second: {1}, Third: {2}", (First == null) ? "null" : First.ToString(), (Second == null) ? "null" : Second.ToString(), (Third == null) ? "null" : Third.ToString()); } /// /// Determines if two triples are equal. Two triples are equal if the all three elements /// compare equal using IComparable<T>.Equals or object.Equals. /// /// First triple to compare. /// Second triple to compare. /// True if the triples are equal. False if the triples are not equal. public static bool operator ==(Triple pair1, Triple pair2) { return pair1.Equals(pair2); } /// /// Determines if two triples are not equal. Two triples are equal if the all three elements /// compare equal using IComparable<T>.Equals or object.Equals. /// /// First triple to compare. /// Second triple to compare. /// True if the triples are not equal. False if the triples are equal. public static bool operator !=(Triple pair1, Triple pair2) { return ! pair1.Equals(pair2); } } }