diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj b/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj
index a93c8e4aadc9d..5312108604e3c 100644
--- a/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj
+++ b/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj
@@ -70,6 +70,7 @@
+
@@ -253,6 +254,7 @@
+
diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs
index de3b12258cca6..24361c55c829e 100644
--- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs
+++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs
@@ -204,6 +204,15 @@ private static EnumConverter CreateEnumConverter(Type type)
private static Hashtable ExtendedPropertyCache => LazyInitializer.EnsureInitialized(ref s_extendedPropertyCache, () => new Hashtable());
+ /// Clear the global caches this maintains on top of reflection.
+ internal static void ClearReflectionCaches()
+ {
+ s_propertyCache = null;
+ s_eventCache = null;
+ s_attributeCache = null;
+ s_extendedPropertyCache = null;
+ }
+
///
/// Adds an editor table for the given editor base type.
/// Typically, editors are specified as metadata on an object. If no metadata for a
diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectionCachesUpdateHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectionCachesUpdateHandler.cs
new file mode 100644
index 0000000000000..fe2a5306553a9
--- /dev/null
+++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectionCachesUpdateHandler.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#nullable enable
+using System.ComponentModel;
+using System.Reflection;
+using System.Reflection.Metadata;
+
+[assembly: MetadataUpdateHandler(typeof(ReflectionCachesUpdateHandler))]
+
+namespace System.ComponentModel
+{
+ internal static class ReflectionCachesUpdateHandler
+ {
+ public static void BeforeUpdate(Type[]? types)
+ {
+ // ReflectTypeDescriptionProvider maintains global caches on top of reflection.
+ // Clear those.
+ ReflectTypeDescriptionProvider.ClearReflectionCaches();
+
+ // Each type descriptor may also cache reflection-based state that it gathered
+ // from ReflectTypeDescriptionProvider. Clear those as well.
+ if (types is not null)
+ {
+ foreach (Type type in types)
+ {
+ TypeDescriptor.Refresh(type);
+ }
+ }
+ else
+ {
+ foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
+ {
+ TypeDescriptor.Refresh(assembly);
+ }
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/ReflectionCachesUpdateHandlerTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/ReflectionCachesUpdateHandlerTests.cs
new file mode 100644
index 0000000000000..829bbd5b1edce
--- /dev/null
+++ b/src/libraries/System.ComponentModel.TypeConverter/tests/ReflectionCachesUpdateHandlerTests.cs
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Reflection;
+using Xunit;
+
+namespace System.ComponentModel.Tests
+{
+ [SimpleUpdateTest]
+ public class ReflectionCachesUpdateHandlerTests
+ {
+ [Fact]
+ public void ReflectionCachesUpdateHandler_CachesCleared()
+ {
+ AttributeCollection ac1 = TypeDescriptor.GetAttributes(typeof(ReflectionCachesUpdateHandlerTests));
+ AttributeCollection ac2 = TypeDescriptor.GetAttributes(typeof(ReflectionCachesUpdateHandlerTests));
+ Assert.Equal(ac1.Count, ac2.Count);
+ Assert.Equal(1, ac1.Count);
+ Assert.Same(ac1[0], ac2[0]);
+
+ MethodInfo beforeUpdate = typeof(TypeDescriptionProvider).Assembly.GetType("System.ComponentModel.ReflectionCachesUpdateHandler", throwOnError: true).GetMethod("BeforeUpdate");
+ Assert.NotNull(beforeUpdate);
+ beforeUpdate.Invoke(null, new object[] { null });
+
+ AttributeCollection ac3 = TypeDescriptor.GetAttributes(typeof(ReflectionCachesUpdateHandlerTests));
+ Assert.NotSame(ac1[0], ac3[0]);
+ }
+ }
+
+ [AttributeUsage(AttributeTargets.All)]
+ internal sealed class SimpleUpdateTestAttribute : Attribute { }
+}
diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj b/src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj
index b8a634b041a8f..4f96c81aea140 100644
--- a/src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj
+++ b/src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj
@@ -104,6 +104,7 @@
+