Dave said:
I seem to be missing one of my favorite STL techniques for key, value
collections - being able to use Composite Keys. What i mean by C.K.
is the ability for a key to compare multiple values when sorting /
searching collections.
E.g. IP Address, and port as key, rather than the single IP Address
value.
in STL i would create an object as key, and override the "<" operator
to compare the composite key (multiple values) - thereby achieving
what i needed. (STL often used equivalence rather than equality for
its collections - hence less than operator overloading)
Can I do something similar in C#?
thanks tons, dave
p.s. i could crudely append values IPAddr and Port# together making a
pseudo composite key, but not pretty.
Here is my own implementation pre-C#3 that allowed me to do that kind of
stuff.
Note that it suffers from the problem Jon Skeet alludes to, where the
hash code is simply xoring the individual members.
--
Lasse Vågsæther Karlsen
mailto:
[email protected]
http://presentationmode.blogspot.com/
PGP KeyID: 0xBCDEA2E3
namespace LVK.GenericTypes
{
#region Structure<T1, T2>
/// <summary>
/// Defines a structure with two fields.
/// </summary>
/// <typeparam name="T1">
/// The type of the <see cref="Structure{T1, T2}.Value1"/> field.
/// </typeparam>
/// <typeparam name="T2">
/// The type of the <see cref="Structure{T1, T2}.Value2"/> field.
/// </typeparam>
public struct Structure<T1, T2> : IComparable<Structure<T1, T2>>
{
#region Public Fields
/// <summary>
/// The first value in the structure.
/// </summary>
public T1 Value1;
/// <summary>
/// The second value in the structure.
/// </summary>
public T2 Value2;
#endregion
#region Construction & Destruction
/// <summary>
/// Constructs a new structure of type <see
cref="Structure{T1,T2}"/> and initializes the values.
/// </summary>
/// <param name="value1">
/// The first value in the structure.
/// </param>
/// <param name="value2">
/// The second value in the structure.
/// </param>
public Structure(T1 value1, T2 value2)
{
Value1 = value1;
Value2 = value2;
}
#endregion
#region Equals and GetHashCode
/// <summary>
/// Indicates whether this instance and a specified object are
equal.
/// </summary>
/// <param name="other">Another <see cref="Structure{T1, T2}"/>
to compare to.</param>
/// <returns>
/// <c>true</c> if <paramref name="other"/> and this instance
represent the same value; otherwise, <c>false</c>.
/// </returns>
public Boolean Equals(Structure<T1, T2> other)
{
// Compare Value1
if ((ReferenceEquals(Value1, null)) !=
(ReferenceEquals(other.Value1, null)))
return false;
if (!ReferenceEquals(Value1, null) && Value1 is
IComparable<T1> &&
((IComparable<T1>)Value1).CompareTo(other.Value1) != 0)
return false;
// Compare Value2
if ((ReferenceEquals(Value2, null)) !=
(ReferenceEquals(other.Value2, null)))
return false;
if (!ReferenceEquals(Value2, null) && Value2 is
IComparable<T2> &&
((IComparable<T2>)Value2).CompareTo(other.Value2) != 0)
return false;
return true;
}
/// <summary>
/// Indicates whether this instance and a specified object are
equal.
/// </summary>
/// <param name="obj">Another object to compare to.</param>
/// <returns>
/// <c>true</c> if obj and this instance are the same type and
represent the same value; otherwise, <c>false</c>.
/// </returns>
public override Boolean Equals(Object obj)
{
if (obj is Structure<T1, T2>)
return Equals((Structure<T1, T2>)obj);
else
return false;
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>
/// A 32-bit signed integer that is the hash code for this
instance.
/// </returns>
/// <remarks>
/// Note that this method returns a hash code based on the
values of the fields in the structure.
/// Thus, if the values of these fields change, the structure
will have a new hash code, usually
/// this will not pose a problem since a dictionary that uses
one of these as a key will make its own
/// copy of the contents of the struct.
/// </remarks>
public override Int32 GetHashCode()
{
Int32 hashcode = 0;
if (!ReferenceEquals(Value1, null))
hashcode ^= Value1.GetHashCode();
if (!ReferenceEquals(Value2, null))
hashcode ^= Value2.GetHashCode();
return hashcode;
}
#endregion
#region IComparable<Structure<T1,T2>> Members
/// <summary>
/// Compares the current object with another object of the same
type.
/// </summary>
/// <param name="other">An object to compare with this
object.</param>
/// <returns>
/// A 32-bit signed integer that indicates the relative order
of the objects being compared.
/// If <c>this</c> object is less than <paramref
name="other"/>, a negative value will be returned.
/// If <c>this</c> object is greater than <paramref
name="other"/>, a positive value will be returned.
/// Otherwise, zero will be returned to indicate that
<c>this</c> is equal to <paramref name="other"/>.
/// </returns>
public Int32 CompareTo(Structure<T1, T2> other)
{
Int32 result;
if ((ReferenceEquals(Value1, null)) !=
(ReferenceEquals(other.Value1, null)))
return (ReferenceEquals(Value1, null)) ? -1 : +1;
if (!ReferenceEquals(Value1, null) && Value1 is
IComparable<T1>)
{
result = ((IComparable<T1>)Value1).CompareTo(other.Value1);
if (result != 0)
return result;
}
if ((ReferenceEquals(Value2, null)) !=
(ReferenceEquals(other.Value2, null)))
return (ReferenceEquals(Value2, null)) ? -1 : +1;
if (!ReferenceEquals(Value2, null) && Value2 is
IComparable<T2>)
{
result = ((IComparable<T2>)Value2).CompareTo(other.Value2);
if (result != 0)
return result;
}
return 0;
}
#endregion
#region Overridden Methods
/// <summary>
/// Returns the value of the structure as a string, containing
the values of the individual fields all
/// concatenated to a string in braces.
/// </summary>
/// <returns>
/// A <see cref="String"></see> containing the values of all
the fields in the structure.
/// </returns>
public override String ToString()
{
return String.Format("{{ {0}, {1} }}", Value1, Value2);
}
#endregion
#region Operators
/// <summary>
/// Determines wether two <see cref="Structure{T1, T2}"/>
contains the same values
/// in their fields.
/// </summary>
/// <param name="value1">
/// The first <see cref="Structure{T1, T2}"/> value to compare
to <paramref name="value2"/>.
/// </param>
/// <param name="value2">
/// The second <see cref="Structure{T1, T2}"/> value to compare
to <paramref name="value1"/>.
/// </param>
/// <returns>
/// <c>true</c> if the two structures <paramref name="value1"/>
and <paramref name="value2"/>
/// contains the same values in their fields; otherwise,
<c>false</c>.
/// </returns>
/// <remarks>
/// Equivalent to <c><paramref name="value1"/>.Equals(<paramref
name="value2"/>)</c>.
/// </remarks>
public static Boolean operator ==(Structure<T1, T2> value1,
Structure<T1, T2> value2)
{
return value1.Equals(value2);
}
/// <summary>
/// Determines wether two <see cref="Structure{T1, T2}"/> does
not contain the same values
/// in their fields.
/// </summary>
/// <param name="value1">
/// The first <see cref="Structure{T1, T2}"/> value to compare
to <paramref name="value2"/>.
/// </param>
/// <param name="value2">
/// The second <see cref="Structure{T1, T2}"/> value to compare
to <paramref name="value1"/>.
/// </param>
/// <returns>
/// <c>true</c> if the two structures <paramref name="value1"/>
and <paramref name="value2"/>
/// does not contain the same values in their fields;
otherwise, <c>false</c>.
/// </returns>
/// <remarks>
/// Equivalent to <c>!(<paramref name="value1"/> == <paramref
name="value2"/>)</c>.
/// </remarks>
public static Boolean operator !=(Structure<T1, T2> value1,
Structure<T1, T2> value2)
{
return !(value1 == value2);
}
#endregion
}
#endregion
}