/// <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));
        }
Example #2
0
        /// <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));
        }
Example #3
0
        // Equals method for the comparer itself.
        public override bool Equals(Object obj)
        {
            ByteEqualityComparer comparer = obj as ByteEqualityComparer;

            return(comparer != null);
        }
Example #4
0
        [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
        }