From 71ce2416744ba50cc04004bc2d28a484ed948cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 24 Sep 2026 13:09:09 +0900 Subject: [PATCH 1/2] ParameterInfo identity across hot reload --- .../System/Reflection/ReflectionCacheTests.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/ReflectionCacheTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/ReflectionCacheTests.cs index a5922112c1d98e..b1d5961a7afa7f 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/ReflectionCacheTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/ReflectionCacheTests.cs @@ -138,6 +138,42 @@ public void GetMembers_MultipleCalls_ClearCache_ReflectionCacheTestsType() }, options); } + [ActiveIssue("https://github.com/dotnet/runtime/issues/50978", TestRuntimes.Mono)] + [ConditionalFact(typeof(ReflectionCacheTests), nameof(IsMetadataUpdateAndRemoteExecutorSupported))] + public void GetParameters_ClearCache_EqualsAndHashCodeEqual() + { + RemoteInvokeOptions options = new RemoteInvokeOptions(); + options.StartInfo.EnvironmentVariables.Add("DOTNET_MODIFIABLE_ASSEMBLIES", "debug"); + + using RemoteInvokeHandle remoteHandle = RemoteExecutor.Invoke(() => + { + Action clearCache = GetClearCacheMethod(); + MethodInfo method1 = s_type.GetMethod(nameof(Method)); + ParameterInfo parameter1 = method1.GetParameters()[0]; + var valuesByParameter = new System.Collections.Generic.Dictionary + { + [parameter1] = 1 + }; + + clearCache(new[] { typeof(ReflectionCacheTests) }); + + MethodInfo method2 = s_type.GetMethod(nameof(Method)); + ParameterInfo parameter2 = method2.GetParameters()[0]; + + Assert.NotSame(method1, method2); + Assert.True(method1.Equals(method2)); + Assert.NotSame(parameter1, parameter2); + + // Parameters for equal members at the same position should remain equal + // after Hot Reload clears the reflection cache, including as dictionary keys. + Assert.True(parameter1.Equals(parameter2)); + Assert.True(parameter2.Equals(parameter1)); + Assert.Equal(parameter1.GetHashCode(), parameter2.GetHashCode()); + Assert.True(valuesByParameter.TryGetValue(parameter2, out int value)); + Assert.Equal(1, value); + }, options); + } + private static void AssertNotSameSameButEqualAndHashCodeEqual(object o1, object o2) { // After the cache cleared the references of the same members will be Not Same. From e7773430878ae1f4d1e93244343206f00b559128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 24 Sep 2026 16:26:55 +0900 Subject: [PATCH 2/2] Override Equals and GetHashCode in RuntimeParameterInfo --- .../src/System/Reflection/RuntimeParameterInfo.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs index d46d596ecec63d..bfc00b14fa7e59 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs @@ -211,6 +211,14 @@ internal RuntimeParameterInfo(MethodInfo owner, string? name, Type parameterType #endregion #region Public Methods + public override bool Equals(object? obj) => + obj is RuntimeParameterInfo other && + PositionImpl == other.PositionImpl && + MemberImpl.Equals(other.MemberImpl); + + public override int GetHashCode() => + HashCode.Combine(MemberImpl, PositionImpl); + public override Type ParameterType { get