diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs index c42d636cb7f28..3f0c7a38d0cf4 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs @@ -293,22 +293,17 @@ private static void CheckDefaultProvider(Type type) contextForAdd = new ResetEventContext(resetEventForAdd, Environment.CurrentManagedThreadId); s_processedTypes.Add(type, contextForAdd); - // Always use core reflection when checking for - // the default provider attribute. If there is a - // provider, we probably don't want to build up our - // own cache state against the type. There shouldn't be - // more than one of these, but walk anyway. Walk in - // reverse order so that the most derived takes precidence. - var attrs = type.GetCustomAttributes(false) - .ToArray(); + var providerAttr = type.GetCustomAttributes(false) + .SingleOrDefault(); bool providerAdded = false; - for (int i = 0; i != attrs.Length; i++) + + if (providerAttr != null) { - Type? providerType = Type.GetType(attrs[i].TypeName); + Type? providerType = Type.GetType(providerAttr.TypeName); if (providerType != null && typeof(TypeDescriptionProvider).IsAssignableFrom(providerType)) { - TypeDescriptionProvider prov = (TypeDescriptionProvider)Activator.CreateInstance(providerType)!; - AddProvider(prov, type); + TypeDescriptionProvider provider = (TypeDescriptionProvider)Activator.CreateInstance(providerType)!; + AddProvider(provider, type); providerAdded = true; } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptionProviderTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptionProviderTests.cs index 8d1e6a2cff897..7b7cfd7fb05b1 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptionProviderTests.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptionProviderTests.cs @@ -810,18 +810,35 @@ public void IsSupportedType_NullTypeWithParent_ThrowsArgumentNullException() AssertExtensions.Throws("type", () => provider.IsSupportedType(null)); } - [Fact] - public async void GetConverter_ByMultithread_ReturnsExpected() + public static IEnumerable GetConverter_ByMultithread_ReturnsExpected_TestData() + { + yield return new object[] { typeof(MyClass), typeof(MyTypeConverter) }; + yield return new object[] { typeof(MyInheritedClassWithCustomTypeDescriptionProvider), typeof(MyInheritedClassWithCustomTypeDescriptionProviderConverter) }; + yield return new object[] { typeof(MyInheritedClassWithInheritedTypeDescriptionProvider), typeof(MyTypeConverter) }; + } + + [Theory] + [MemberData(nameof(GetConverter_ByMultithread_ReturnsExpected_TestData))] + public async void GetConverter_ByMultithread_ReturnsExpected(Type typeForGetConverter, Type expectedConverterType) { TypeConverter[] actualConverters = await Task.WhenAll( Enumerable.Range(0, 100).Select(_ => - Task.Run(() => TypeDescriptor.GetConverter(typeof(MyClass))))); + Task.Run(() => TypeDescriptor.GetConverter(typeForGetConverter)))); Assert.All(actualConverters, - currentConverter => Assert.IsType(currentConverter)); + currentConverter => Assert.IsType(expectedConverterType, currentConverter)); } - [Fact] - public async void GetConverterWithAddProvider_ByMultithread_Success() + public static IEnumerable GetConverterWithAddProvider_ByMultithread_Success_TestData() + { + foreach (object[] currentTestCase in GetConverter_ByMultithread_ReturnsExpected_TestData()) + { + yield return currentTestCase; + } + } + + [Theory] + [MemberData(nameof(GetConverterWithAddProvider_ByMultithread_Success_TestData))] + public async void GetConverterWithAddProvider_ByMultithread_Success(Type typeForGetConverter, Type expectedConverterType) { TypeConverter[] actualConverters = await Task.WhenAll( Enumerable.Range(0, 200).Select(_ => @@ -830,10 +847,10 @@ public async void GetConverterWithAddProvider_ByMultithread_Success() var mockProvider = new Mock(MockBehavior.Strict); var someInstance = new object(); TypeDescriptor.AddProvider(mockProvider.Object, someInstance); - return TypeDescriptor.GetConverter(typeof(MyClass)); + return TypeDescriptor.GetConverter(typeForGetConverter); }))); Assert.All(actualConverters, - currentConverter => Assert.IsType(currentConverter)); + currentConverter => Assert.IsType(expectedConverterType, currentConverter)); } [TypeDescriptionProvider(typeof(MyClassTypeDescriptionProvider))] @@ -841,6 +858,15 @@ public class MyClass { } + [TypeDescriptionProvider(typeof(MyInheritedClassWithCustomTypeDescriptionProviderTypeDescriptionProvider))] + public class MyInheritedClassWithCustomTypeDescriptionProvider : MyClass + { + } + + public class MyInheritedClassWithInheritedTypeDescriptionProvider : MyClass + { + } + public class MyClassTypeDescriptionProvider : TypeDescriptionProvider { public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) @@ -849,6 +875,14 @@ public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object } } + public class MyInheritedClassWithCustomTypeDescriptionProviderTypeDescriptionProvider : TypeDescriptionProvider + { + public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) + { + return new MyInheritedClassWithCustomTypeDescriptionProviderTypeDescriptor(); + } + } + public class MyClassTypeDescriptor : CustomTypeDescriptor { public override TypeConverter GetConverter() @@ -857,10 +891,22 @@ public override TypeConverter GetConverter() } } + public class MyInheritedClassWithCustomTypeDescriptionProviderTypeDescriptor : CustomTypeDescriptor + { + public override TypeConverter GetConverter() + { + return new MyInheritedClassWithCustomTypeDescriptionProviderConverter(); + } + } + public class MyTypeConverter : TypeConverter { } + public class MyInheritedClassWithCustomTypeDescriptionProviderConverter : TypeConverter + { + } + private class SubTypeDescriptionProvider : TypeDescriptionProvider { public SubTypeDescriptionProvider() : base()