//****************************** // 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 pair 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 Pair : 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 IEqualityComparer firstEqualityComparer = EqualityComparer.Default; private static IEqualityComparer secondEqualityComparer = EqualityComparer.Default; /// /// The first element of the pair. /// public TFirst First; /// /// The second element of the pair. /// public TSecond Second; /// /// Creates a new pair with given first and second elements. /// /// The first element of the pair. /// The second element of the pair. public Pair(TFirst first, TSecond second) { this.First = first; this.Second = second; } /// /// Creates a new pair using elements from a KeyValuePair structure. The /// First element gets the Key, and the Second elements gets the Value. /// /// The KeyValuePair to initialize the Pair with . public Pair(KeyValuePair keyAndValue) { this.First = keyAndValue.Key; this.Second = keyAndValue.Value; } /// /// Determines if this pair is equal to another object. The pair is equal to another object /// if that object is a Pair, both element types are the same, and the first and second elements /// both 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 Pair) { Pair other = (Pair)obj; return Equals(other); } else { return false; } } /// /// Determines if this pair is equal to another pair. The pair is equal if the first and second elements /// both compare equal using IComparable<T>.Equals or object.Equals. /// /// Pair to compare with for equality. /// True if the pairs are equal. False if the pairs are not equal. public bool Equals(Pair other) { return firstEqualityComparer.Equals(First, other.First) && secondEqualityComparer.Equals(Second, other.Second); } /// /// Returns a hash code for the pair, suitable for use in a hash-table or other hashed collection. /// Two pairs that compare equal (using Equals) will have the same hash code. The hash code for /// the pair is derived by combining the hash codes for each of the two elements of the pair. /// /// 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(); return hashFirst ^ hashSecond; } /// /// Compares this pair to another pair of the some type. The pairs are compared by using /// the IComparable<T> or IComparable interface on TFirst and TSecond. The pairs /// are compared by their first elements first, if their first elements are equal, then they /// are compared by their second elements. /// If either TFirst or TSecond does not implement IComparable<T> or IComparable, then /// an NotSupportedException is thrown, because the pairs cannot be compared. /// /// The pair to compare to. /// An integer indicating how this pair compares to . Less /// than zero indicates this pair is less than . Zero indicate this pair is /// equals to . Greater than zero indicates this pair is greater than /// . /// Either FirstSecond or TSecond is not comparable /// via the IComparable<T> or IComparable interfaces. public int CompareTo(Pair other) { try { int firstCompare = firstComparer.Compare(First, other.First); if (firstCompare != 0) return firstCompare; else return secondComparer.Compare(Second, other.Second); } 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 throw; // Hmmm. Unclear why we got the ArgumentException. } } /// /// Compares this pair to another pair of the some type. The pairs are compared by using /// the IComparable<T> or IComparable interface on TFirst and TSecond. The pairs /// are compared by their first elements first, if their first elements are equal, then they /// are compared by their second elements. /// If either TFirst or TSecond does not implement IComparable<T> or IComparable, then /// an NotSupportedException is thrown, because the pairs cannot be compared. /// /// The pair to compare to. /// An integer indicating how this pair compares to . Less /// than zero indicates this pair is less than . Zero indicate this pair is /// equals to . Greater than zero indicates this pair is greater than /// . /// is not of the correct type. /// Either FirstSecond or TSecond is not comparable /// via the IComparable<T> or IComparable interfaces. int IComparable.CompareTo(object obj) { if (obj is Pair) return CompareTo((Pair)obj); else throw new ArgumentException(Strings.BadComparandType, "obj"); } /// /// Returns a string representation of the pair. The string representation of the pair is /// of the form: /// First: {0}, Second: {1} /// where {0} is the result of First.ToString(), and {1} is the result of Second.ToString() (or /// "null" if they are null.) /// /// The string representation of the pair. public override string ToString() { return string.Format("First: {0}, Second: {1}", (First == null) ? "null" : First.ToString(), (Second == null) ? "null" : Second.ToString()); } /// /// Determines if two pairs are equal. Two pairs are equal if the first and second elements /// both compare equal using IComparable<T>.Equals or object.Equals. /// /// First pair to compare. /// Second pair to compare. /// True if the pairs are equal. False if the pairs are not equal. public static bool operator ==(Pair pair1, Pair pair2) { return firstEqualityComparer.Equals(pair1.First, pair2.First) && secondEqualityComparer.Equals(pair1.Second, pair2.Second); } /// /// Determines if two pairs are not equal. Two pairs are equal if the first and second elements /// both compare equal using IComparable<T>.Equals or object.Equals. /// /// First pair to compare. /// Second pair to compare. /// True if the pairs are not equal. False if the pairs are equal. public static bool operator !=(Pair pair1, Pair pair2) { return !(pair1 == pair2); } /// /// Converts a Pair to a KeyValuePair. The Key part of the KeyValuePair gets /// the First element, and the Value part of the KeyValuePair gets the Second /// elements. /// /// Pair to convert. /// The KeyValuePair created from . public static explicit operator KeyValuePair(Pair pair) { return new KeyValuePair(pair.First, pair.Second); } /// /// Converts this Pair to a KeyValuePair. The Key part of the KeyValuePair gets /// the First element, and the Value part of the KeyValuePair gets the Second /// elements. /// /// The KeyValuePair created from this Pair. public KeyValuePair ToKeyValuePair() { return new KeyValuePair(this.First, this.Second); } /// /// Converts a KeyValuePair structure into a Pair. The /// First element gets the Key, and the Second element gets the Value. /// /// The KeyValuePair to convert. /// The Pair created by converted the KeyValuePair into a Pair. public static explicit operator Pair(KeyValuePair keyAndValue) { return new Pair(keyAndValue); } } }