diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdFieldInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdFieldInfo.cs index 0455735183b4c6..1e2f383c6d5524 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdFieldInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdFieldInfo.cs @@ -53,7 +53,7 @@ obj is MdFieldInfo fi && ReferenceEquals(fi.m_reflectedTypeCache.GetRuntimeType(), m_reflectedTypeCache.GetRuntimeType())); public override int GetHashCode() => - HashCode.Combine(m_tkField.GetHashCode(), m_declaringType.GetUnderlyingNativeHandle().GetHashCode()); + HashCode.Combine(m_tkField, m_declaringType.GetUnderlyingNativeHandle(), m_reflectedTypeCache.GetRuntimeType().GetUnderlyingNativeHandle()); #endregion #region FieldInfo Overrides diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs index cf27a2aaba39d5..e2588c2c300f56 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs @@ -74,7 +74,7 @@ obj is RtFieldInfo fi && ReferenceEquals(fi.m_reflectedTypeCache.GetRuntimeType(), m_reflectedTypeCache.GetRuntimeType())); public override int GetHashCode() => - HashCode.Combine(m_fieldHandle.GetHashCode(), m_declaringType.GetUnderlyingNativeHandle().GetHashCode()); + HashCode.Combine(m_fieldHandle, m_reflectedTypeCache.GetRuntimeType().GetUnderlyingNativeHandle()); #endregion diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.CoreCLR.cs index 576283e807529a..0c5de0c21006a1 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.CoreCLR.cs @@ -122,7 +122,7 @@ public override bool Equals(object? obj) => (MetadataUpdater.IsSupported && CacheEquals(obj)); public override int GetHashCode() => - HashCode.Combine(m_handle.GetHashCode(), m_declaringType.GetUnderlyingNativeHandle().GetHashCode()); + HashCode.Combine(m_handle, m_declaringType.GetUnderlyingNativeHandle()); #endregion #region ICustomAttributeProvider diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs index cdef2ad754f7a8..c026ed1b33ee31 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs @@ -82,7 +82,7 @@ obj is RuntimeEventInfo ei && ReferenceEquals(ei.m_reflectedTypeCache.GetRuntimeType(), m_reflectedTypeCache.GetRuntimeType())); public override int GetHashCode() => - HashCode.Combine(m_token.GetHashCode(), m_declaringType.GetUnderlyingNativeHandle().GetHashCode()); + HashCode.Combine(m_token, m_declaringType.GetUnderlyingNativeHandle(), m_reflectedTypeCache.GetRuntimeType().GetUnderlyingNativeHandle()); #endregion #region ICustomAttributeProvider diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.CoreCLR.cs index 5386568d617e99..374579b9584b31 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.CoreCLR.cs @@ -159,11 +159,10 @@ public override string ToString() // retrieve items from and insert items into s_methodInstantiations. public override int GetHashCode() => - HashCode.Combine(m_handle.GetHashCode(), m_declaringType.GetUnderlyingNativeHandle().GetHashCode()); + HashCode.Combine(m_handle, m_reflectedTypeCache.GetRuntimeType().GetUnderlyingNativeHandle()); public override bool Equals(object? obj) => obj is RuntimeMethodInfo m && m_handle == m.m_handle && - ReferenceEquals(m_declaringType, m.m_declaringType) && ReferenceEquals(m_reflectedTypeCache.GetRuntimeType(), m.m_reflectedTypeCache.GetRuntimeType()); #endregion diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs index bca5b8f506c0e9..3474e4ade2bfc1 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs @@ -190,7 +190,7 @@ public override bool Equals(object? obj) => ReferenceEquals(rpi.m_reflectedTypeCache.GetRuntimeType(), m_reflectedTypeCache.GetRuntimeType())); public override int GetHashCode() => - HashCode.Combine(m_token.GetHashCode(), m_declaringType.GetUnderlyingNativeHandle().GetHashCode()); + HashCode.Combine(m_token, m_declaringType.GetUnderlyingNativeHandle(), m_reflectedTypeCache.GetRuntimeType().GetUnderlyingNativeHandle()); #endregion #region PropertyInfo Overrides diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/EventInfos/NativeFormat/NativeFormatRuntimeEventInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/EventInfos/NativeFormat/NativeFormatRuntimeEventInfo.cs index 43dc9103043a4d..10aa5f447ddaee 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/EventInfos/NativeFormat/NativeFormatRuntimeEventInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/EventInfos/NativeFormat/NativeFormatRuntimeEventInfo.cs @@ -133,7 +133,7 @@ public sealed override bool Equals(object obj) public sealed override int GetHashCode() { - return _eventHandle.GetHashCode(); + return HashCode.Combine(_eventHandle, ContextTypeInfo, ReflectedType); } public sealed override Type EventHandlerType diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs index fde55471130bb6..b0f481d79ed9fa 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs @@ -121,7 +121,7 @@ public sealed override bool Equals(object obj) public sealed override int GetHashCode() { - return _fieldHandle.GetHashCode(); + return HashCode.Combine(_fieldHandle, _contextTypeInfo, _reflectedType); } public sealed override RuntimeFieldHandle FieldHandle diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructedGenericMethodInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructedGenericMethodInfo.cs index 2a151dc9d77f08..e5fc346552fda5 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructedGenericMethodInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructedGenericMethodInfo.cs @@ -72,7 +72,17 @@ public sealed override bool Equals(object obj) public sealed override int GetHashCode() { - return _genericMethodDefinition.GetHashCode(); + var hashcode = default(HashCode); + + hashcode.Add(_genericMethodDefinition); + hashcode.Add(_genericTypeArguments.Length); + + for (int i = 0; i < _genericTypeArguments.Length; i++) + { + hashcode.Add(_genericTypeArguments[i]); + } + + return hashcode.ToHashCode(); } internal sealed override int GenericParameterCount => _genericMethodDefinition.GenericParameterCount; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs index b8ae37e6981619..25528aa3d1af8c 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs @@ -222,7 +222,7 @@ public sealed override bool Equals(object obj) public sealed override int GetHashCode() { - return _common.GetHashCode(); + return HashCode.Combine(_common, _reflectedType); } public sealed override RuntimeMethodHandle MethodHandle => GetRuntimeMethodHandle(null); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticMethodInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticMethodInfo.cs index 3efdeb59835dca..3d5dcb887f253d 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticMethodInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticMethodInfo.cs @@ -80,7 +80,7 @@ public sealed override MethodInfo GetGenericMethodDefinition() public sealed override int GetHashCode() { - return _declaringType.GetHashCode(); + return HashCode.Combine(_syntheticMethodId, _declaringType); } public sealed override bool IsConstructedGenericMethod diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/PropertyInfos/NativeFormat/NativeFormatRuntimePropertyInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/PropertyInfos/NativeFormat/NativeFormatRuntimePropertyInfo.cs index 0a87f69cf6ffd8..bc44c7df03804e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/PropertyInfos/NativeFormat/NativeFormatRuntimePropertyInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/PropertyInfos/NativeFormat/NativeFormatRuntimePropertyInfo.cs @@ -113,7 +113,7 @@ public sealed override bool Equals(object obj) public sealed override int GetHashCode() { - return _propertyHandle.GetHashCode(); + return HashCode.Combine(_propertyHandle, ContextTypeInfo, _reflectedType); } public sealed override int MetadataToken diff --git a/src/libraries/System.Runtime/tests/System.Reflection.Tests/EventInfoTests.cs b/src/libraries/System.Runtime/tests/System.Reflection.Tests/EventInfoTests.cs index 7afdfa40374640..a8d22327ac1ada 100644 --- a/src/libraries/System.Runtime/tests/System.Reflection.Tests/EventInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System.Reflection.Tests/EventInfoTests.cs @@ -152,6 +152,27 @@ public void EqualsTest(Type type1, string name1, Type type2, string name2, bool } } + [Fact] + public void GetHashCode_MultipleSubClasses_ShouldBeUnique() + { + var numberOfCollisions = 0; + var hashset = new HashSet(); + + foreach (var type in new Type[] { typeof(BaseClass), typeof(SubClassA), typeof(SubClassB), typeof(SubClassC) }) + { + foreach (var eventInfo in type.GetEvents()) + { + if (!hashset.Add(eventInfo.GetHashCode())) + { + numberOfCollisions++; + } + } + } + + // If intermittent failures are observed, it's acceptable to relax the assertion to allow some collisions. + Assert.Equal(0, numberOfCollisions); + } + [Theory] [MemberData(nameof(Events_TestData))] public void EventHandlerType(Type type, string name) @@ -193,6 +214,9 @@ protected class BaseClass public event EventHandler PublicEvent; public static event EventHandler PublicStaticEvent; public virtual event EventHandler PublicVirtualEvent; + public event EventHandler PublicEvent1; + public event EventHandler PublicEvent2; + public event EventHandler PublicEvent3; } protected class SubClass : BaseClass @@ -200,6 +224,9 @@ protected class SubClass : BaseClass public new event EventHandler PublicEvent; public event EventHandler EventPublicNew; } + protected class SubClassA : BaseClass { } + protected class SubClassB : BaseClass { } + protected class SubClassC : BaseClass { } #pragma warning restore 0067 } } diff --git a/src/libraries/System.Runtime/tests/System.Reflection.Tests/FieldInfoTests.cs b/src/libraries/System.Runtime/tests/System.Reflection.Tests/FieldInfoTests.cs index b2d76725e99396..f157374f0a5665 100644 --- a/src/libraries/System.Runtime/tests/System.Reflection.Tests/FieldInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System.Reflection.Tests/FieldInfoTests.cs @@ -333,6 +333,27 @@ public void GetHashCodeTest() Assert.NotEqual(0, fieldInfo.GetHashCode()); } + [Fact] + public void GetHashCode_MultipleSubClasses_ShouldBeUnique() + { + var numberOfCollisions = 0; + var hashset = new HashSet(); + + foreach (var type in new Type[] { typeof(FI_FieldArray), typeof(FI_FieldArraySubClassA), typeof(FI_FieldArraySubClassB), typeof(FI_FieldArraySubClassC) }) + { + foreach (var fieldInfo in type.GetFields()) + { + if (!hashset.Add(fieldInfo.GetHashCode())) + { + numberOfCollisions++; + } + } + } + + // If intermittent failures are observed, it's acceptable to relax the assertion to allow some collisions. + Assert.Equal(0, numberOfCollisions); + } + [Theory] [InlineData(typeof(FieldInfoTests), nameof(FieldInfoTests.intField), typeof(int))] [InlineData(typeof(FieldInfoTests), nameof(FieldInfoTests.stringField), typeof(string))] @@ -736,6 +757,9 @@ public class FI_FieldArray public int[] intArray; public object[] objectArray; } + public class FI_FieldArraySubClassA : FI_FieldArray { } + public class FI_FieldArraySubClassB : FI_FieldArray { } + public class FI_FieldArraySubClassC : FI_FieldArray { } public class FI_GenericClass { public FI_GenericClass() { } } diff --git a/src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodInfoTests.cs b/src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodInfoTests.cs index 9a102cec6b5c4d..59151da843bb30 100644 --- a/src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodInfoTests.cs @@ -346,6 +346,27 @@ public void GetHashCodeTest() Assert.NotEqual(0, methodInfo.GetHashCode()); } + [Fact] + public void GetHashCode_MultipleSubClasses_ShouldBeUnique() + { + var numberOfCollisions = 0; + var hashset = new HashSet(); + + foreach (var type in new Type[] { typeof(MI_BaseClass), typeof(MI_SubClassA), typeof(MI_SubClassB), typeof(MI_SubClassC) }) + { + foreach (var methodInfo in type.GetMethods()) + { + if (!hashset.Add(methodInfo.GetHashCode())) + { + numberOfCollisions++; + } + } + } + + // If intermittent failures are observed, it's acceptable to relax the assertion to allow some collisions. + Assert.Equal(0, numberOfCollisions); + } + public static IEnumerable Invoke_TestData() { yield return new object[] { typeof(MI_BaseClass), nameof(MI_BaseClass.VirtualReturnIntMethod), new MI_BaseClass(), null, 0 }; @@ -808,6 +829,10 @@ public void StringArrayMethod(string[] strArray) { } public void MethodWithAttributes() { } } + public class MI_SubClassA : MI_BaseClass { } + public class MI_SubClassB : MI_BaseClass { } + public class MI_SubClassC : MI_BaseClass { } + public class MethodInfoDummySubClass : MI_BaseClass { public override int VirtualReturnIntMethod() => 1; diff --git a/src/libraries/System.Runtime/tests/System.Reflection.Tests/PropertyInfoTests.cs b/src/libraries/System.Runtime/tests/System.Reflection.Tests/PropertyInfoTests.cs index 6b0eb5e7577f05..1096014b8b4730 100644 --- a/src/libraries/System.Runtime/tests/System.Reflection.Tests/PropertyInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System.Reflection.Tests/PropertyInfoTests.cs @@ -292,6 +292,27 @@ public void GetHashCodeTest() Assert.NotEqual(0, propertyInfo.GetHashCode()); } + [Fact] + public void GetHashCode_MultipleSubClasses_ShouldBeUnique() + { + var numberOfCollisions = 0; + var hashset = new HashSet(); + + foreach (var type in new Type[] { typeof(BaseClass), typeof(SubClassA), typeof(SubClassB), typeof(SubClassC) }) + { + foreach (var propertyInfo in type.GetProperties()) + { + if (!hashset.Add(propertyInfo.GetHashCode())) + { + numberOfCollisions++; + } + } + } + + // If intermittent failures are observed, it's acceptable to relax the assertion to allow some collisions. + Assert.Equal(0, numberOfCollisions); + } + [Theory] [InlineData(typeof(BaseClass), "Item", new string[] { "Index" })] [InlineData(typeof(BaseClass), nameof(BaseClass.ReadWriteProperty1), new string[0])] @@ -484,6 +505,9 @@ public string Description set { _description = value; } } } + public class SubClassA : BaseClass { } + public class SubClassB : BaseClass { } + public class SubClassC : BaseClass { } public class CustomIndexerNameClass {