internalstruct Slot { internalint hashCode; // Lower 31 bits of hash code, -1 if unused internalint next; // Index of next entry, -1 if last internal T value; }
public interface IEqualityComparer<in T> { boolEquals(T x, T y); intGetHashCode(T obj); }
privatevoidIncreaseCapacity() { Debug.Assert(m_buckets != null, "IncreaseCapacity called on a set with no elements"); int newSize = HashHelpers.ExpandPrime(m_count); if (newSize <= m_count) { thrownew ArgumentException(SR.GetString(SR.Arg_HSCapacityOverflow)); } // Able to increase capacity; copy elements to larger array and rehash SetCapacity(newSize, false); }
publicstaticintExpandPrime(int oldSize) { int newSize = 2 * oldSize; // Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow. // Note that this check works even when _items.Length overflowed thanks to the (uint) cast if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize) { Contract.Assert( MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength"); return MaxPrimeArrayLength; } return GetPrime(newSize); }
privatestatic EqualityComparer<T> CreateComparer() { Contract.Ensures(Contract.Result<EqualityComparer<T>>() != null); RuntimeType t = (RuntimeType)typeof(T); // Specialize type byte for performance reasons if (t == typeof(byte)) { return (EqualityComparer<T>)(object)(new ByteEqualityComparer()); } // If T implements IEquatable<T> return a GenericEqualityComparer<T> if (typeof(IEquatable<T>).IsAssignableFrom(t)) { return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericEqualityComparer<int>), t); } // If T is a Nullable<U> where U implements IEquatable<U> return a NullableEqualityComparer<U> if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) { RuntimeType u = (RuntimeType)t.GetGenericArguments()[0]; if (typeof(IEquatable<>).MakeGenericType(u).IsAssignableFrom(u)) { return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(NullableEqualityComparer<int>), u); } } // See the METHOD__JIT_HELPERS__UNSAFE_ENUM_CAST and METHOD__JIT_HELPERS__UNSAFE_ENUM_CAST_LONG cases in getILIntrinsicImplementation if (t.IsEnum) { TypeCode underlyingTypeCode = Type.GetTypeCode(Enum.GetUnderlyingType(t)); // Depending on the enum type, we need to special case the comparers so that we avoid boxing // Note: We have different comparers for Short and SByte because for those types we need to make sure we call GetHashCode on the actual underlying type as the // implementation of GetHashCode is more complex than for the other types. switch (underlyingTypeCode) { case TypeCode.Int16: // short return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ShortEnumEqualityComparer<short>), t); case TypeCode.SByte: return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(SByteEnumEqualityComparer<sbyte>), t); case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Byte: case TypeCode.UInt16: //ushort return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(EnumEqualityComparer<int>), t); case TypeCode.Int64: case TypeCode.UInt64: return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(LongEnumEqualityComparer<long>), t); } } // Otherwise return an ObjectEqualityComparer<T> returnnew ObjectEqualityComparer<T>(); }
//平平无奇的代码 var foo1 = new Foo() { Value = 10, Weight = 1.0M }; var foo2 = new Foo() { Value = 10, Weight = 1.0M }; Assert.AreEqual(true, foo1.Equals(foo2));
//平平无奇的代码 var foo1 = new Foo() { Value = 10, Weight = 1.0M }; var foo2 = new Foo() { Value = 20, Weight = 1.0M }; Assert.IsTrue(foo2.CompareTo(foo1) > 0);
IComparer接口
对于排序来说,理论上有ICompareable 和 ICompareable<T>这两个接口就可以了,为什么还要再定义一组接口呢?其实,我们结合生活中的场景就能想明白,不管是判断两个对象是否相等,还是对两个对象进行排序,这些条件都属于“变量”。ICompareable 和 ICompareable<T>这两个接口设计上的确没什么问题,但这都是一锤子买卖,一旦实现了对应的接口,就意味着如何比较两个对象的逻辑是确定好了的。可生活常识告诉我们,同一组信息不同的人考虑的维度是不一样的,譬如学生的成绩,可以按照某一个科目的成绩来排序,还可以按照各个科目的总成绩甚至是平均分来排序。对于上面的类型 Foo,我们不妨考虑按照 Value 和 Weight 分别进行排序,此时可以这样写:
privatestatic Comparer<T> CreateComparer() { RuntimeType t = (RuntimeType)typeof(T); // If T implements IComparable<T> return a GenericComparer<T> #if FEATURE_LEGACYNETCF // Pre-Apollo Windows Phone call the overload that sorts the keys, not values this achieves the same result if (CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) { if (t.ImplementInterface(typeof(IComparable<T>))) { return (Comparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericComparer<int>), t); } } else #endif if (typeof(IComparable<T>).IsAssignableFrom(t)) { return (Comparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericComparer<int>), t); } // If T is a Nullable<U> where U implements IComparable<U> return a NullableComparer<U> if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) { RuntimeType u = (RuntimeType)t.GetGenericArguments()[0]; if (typeof(IComparable<>).MakeGenericType(u).IsAssignableFrom(u)) { return (Comparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(NullableComparer<int>), u); } }
// Otherwise return an ObjectComparer<T> returnnew ObjectComparer<T>(); }