/// <summary> /// Creates the default <see cref="EqualityComparer{T}"/>. /// </summary> /// <param name="type">The type to create the default equality comparer for.</param> /// <remarks> /// The logic in this method is replicated in vm/compile.cpp to ensure that NGen saves the right instantiations. /// </remarks> internal static object CreateDefaultEqualityComparer(Type type) { Debug.Assert(type != null && type is RuntimeType); object result = null; var runtimeType = (RuntimeType)type; // Specialize for byte so Array.IndexOf is faster. if (type == typeof(byte)) { result = new ByteEqualityComparer(); } // If T implements IEquatable<T> return a GenericEqualityComparer<T> else if (typeof(IEquatable <>).MakeGenericType(type).IsAssignableFrom(type)) { result = CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericEqualityComparer <int>), runtimeType); } // Nullable does not implement IEquatable<T?> directly because that would add an extra interface call per comparison. // Instead, it relies on EqualityComparer<T?>.Default to specialize for nullables and do the lifted comparisons if T implements IEquatable. else if (type.IsGenericType) { if (type.GetGenericTypeDefinition() == typeof(Nullable <>)) { result = TryCreateNullableEqualityComparer(runtimeType); } } // The equality comparer for enums is specialized to avoid boxing. else if (type.IsEnum) { result = TryCreateEnumEqualityComparer(runtimeType); } return(result ?? CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ObjectEqualityComparer <object>), runtimeType)); }
/// <summary> /// Creates the default <see cref="EqualityComparer{T}"/>. /// </summary> /// <param name="type">The type to create the default equality comparer for.</param> /// <remarks> /// The logic in this method is replicated in vm/compile.cpp to ensure that NGen saves the right instantiations. /// </remarks> internal static object CreateDefaultEqualityComparer(Type type) { Debug.Assert(type != null && type is RuntimeType); object?result = null; var runtimeType = (RuntimeType)type; if (type == typeof(byte)) { // Specialize for byte so Array.IndexOf is faster. result = new ByteEqualityComparer(); } else if (type == typeof(string)) { // Specialize for string, as EqualityComparer<string>.Default is on the startup path result = new GenericEqualityComparer <string>(); } else if (type.IsAssignableTo(typeof(IEquatable <>).MakeGenericType(type))) { // If T implements IEquatable<T> return a GenericEqualityComparer<T> result = CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericEqualityComparer <string>), runtimeType); } else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable <>)) { // Nullable does not implement IEquatable<T?> directly because that would add an extra interface call per comparison. var embeddedType = (RuntimeType)type.GetGenericArguments()[0]; result = CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(NullableEqualityComparer <int>), embeddedType); } else if (type.IsEnum) { // The equality comparer for enums is specialized to avoid boxing. result = TryCreateEnumEqualityComparer(runtimeType); } return(result ?? CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ObjectEqualityComparer <object>), runtimeType)); }
// Equals method for the comparer itself. public override bool Equals(Object obj) { ByteEqualityComparer comparer = obj as ByteEqualityComparer; return(comparer != null); }
[System.Security.SecuritySafeCritical] // auto-generated private static EqualityComparer <T> CreateComparer() { Contract.Ensures(Contract.Result <EqualityComparer <T> >() != null); object result = null; RuntimeType t = (RuntimeType)typeof(T); // Specialize type byte for performance reasons if (t == typeof(byte)) { result = new ByteEqualityComparer(); } // If T implements IEquatable<T> return a GenericEqualityComparer<T> else if (typeof(IEquatable <T>).IsAssignableFrom(t)) { result = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericEqualityComparer <int>), t); } else if (default(T) == null) // Reference type/Nullable { // 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)) { result = 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 else 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 result = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ShortEnumEqualityComparer <short>), t); break; case TypeCode.SByte: result = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(SByteEnumEqualityComparer <sbyte>), t); break; case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Byte: case TypeCode.UInt16: //ushort result = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(EnumEqualityComparer <int>), t); break; case TypeCode.Int64: case TypeCode.UInt64: result = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(LongEnumEqualityComparer <long>), t); break; } } return(result != null ? (EqualityComparer <T>)result : new ObjectEqualityComparer <T>()); // Fallback to ObjectEqualityComparer, which uses boxing }