Skip to content

Commit ba008a9

Browse files
Addressed codereview feedback
1 parent d0f61a2 commit ba008a9

File tree

8 files changed

+46
-13
lines changed

8 files changed

+46
-13
lines changed

src/libraries/System.ComponentModel.TypeConverter/System.ComponentModel.TypeConverter.slnx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,11 @@
265265
</Project>
266266
<Project Path="tests/UnloadableTestTypes/UnloadableTestTypes.csproj">
267267
<BuildType Solution="Checked|*" Project="Release" />
268+
<Build Solution="*|arm" Project="false" />
269+
<Build Solution="*|arm64" Project="false" />
270+
<Build Solution="Checked|Any CPU" Project="false" />
271+
<Build Solution="Checked|x64" Project="false" />
272+
<Build Solution="Checked|x86" Project="false" />
268273
</Project>
269274
</Folder>
270275
<Folder Name="/tools/" />

src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<Compile Include="System\ComponentModel\ByteConverter.cs" />
1616
<Compile Include="System\ComponentModel\CharConverter.cs" />
1717
<Compile Include="System\ComponentModel\CollectionConverter.cs" />
18-
<Compile Include="System\ComponentModel\ContextAwareConcurrentHashtable.cs" />
18+
<Compile Include="System\ComponentModel\CollectibleKeyConcurrentHashtable.cs" />
1919
<Compile Include="System\ComponentModel\DateOnlyConverter.cs" />
2020
<Compile Include="System\ComponentModel\DateTimeConverter.cs" />
2121
<Compile Include="System\ComponentModel\DateTimeOffsetConverter.cs" />
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace System.ComponentModel
1515
/// Uses ConditionalWeakTable for the collectible keys (if MemberInfo.IsCollectible is true) and
1616
/// ConcurrentDictionary for non-collectible keys.
1717
/// </summary>
18-
internal sealed class ContextAwareConcurrentHashtable<TKey, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
18+
internal sealed class CollectibleKeyConcurrentHashtable<TKey, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
1919
where TKey : MemberInfo
2020
where TValue : class?
2121
{
@@ -37,12 +37,12 @@ public TValue? this[TKey key]
3737
}
3838
else
3939
{
40-
_collectibleTable.AddOrUpdate(key, value!);
40+
_collectibleTable.AddOrUpdate(key, value);
4141
}
4242
}
4343
}
4444

45-
public bool Contains(TKey key)
45+
public bool ContainsKey(TKey key)
4646
{
4747
return !key.IsCollectible ? _defaultTable.ContainsKey(key) : _collectibleTable.TryGetValue(key, out _);
4848
}
@@ -130,6 +130,7 @@ public void Reset()
130130
{
131131
_defaultEnumerator.Reset();
132132
_collectibleEnumerator.Reset();
133+
_enumeratingCollectibleEnumerator = false;
133134
Current = default;
134135
}
135136
}

src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ContextAwareHashtable.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace System.ComponentModel
1515
internal sealed class ContextAwareHashtable
1616
{
1717
private readonly Hashtable _defaultTable = new Hashtable();
18-
private readonly ConditionalWeakTable<object, object> _collectibleTable = new ConditionalWeakTable<object, object>();
18+
private readonly ConditionalWeakTable<object, object?> _collectibleTable = new ConditionalWeakTable<object, object?>();
1919

2020
public object? this[MemberInfo key]
2121
{
@@ -28,11 +28,11 @@ public object? this[MemberInfo key]
2828
{
2929
if (!key.IsCollectible)
3030
{
31-
_defaultTable[key] = value!;
31+
_defaultTable[key] = value;
3232
}
3333
else
3434
{
35-
_collectibleTable.AddOrUpdate(key, value!);
35+
_collectibleTable.AddOrUpdate(key, value);
3636
}
3737
}
3838
}

src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ namespace System.ComponentModel
2323
internal sealed partial class ReflectTypeDescriptionProvider : TypeDescriptionProvider
2424
{
2525
// ReflectedTypeData contains all of the type information we have gathered for a given type.
26-
private readonly ContextAwareConcurrentHashtable<Type, ReflectedTypeData> _typeData = new ContextAwareConcurrentHashtable<Type, ReflectedTypeData>();
26+
private readonly CollectibleKeyConcurrentHashtable<Type, ReflectedTypeData> _typeData = new CollectibleKeyConcurrentHashtable<Type, ReflectedTypeData>();
2727

2828
// This is the signature we look for when creating types that are generic, but
2929
// want to know what type they are dealing with. Enums are a good example of this;
@@ -981,14 +981,14 @@ private ReflectedTypeData GetTypeDataFromRegisteredType(Type type)
981981
{
982982
Type componentType = typeof(T);
983983

984-
if (_typeData.Contains(componentType))
984+
if (_typeData.ContainsKey(componentType))
985985
{
986986
return;
987987
}
988988

989989
lock (TypeDescriptor.s_commonSyncObject)
990990
{
991-
if (_typeData.Contains(componentType))
991+
if (_typeData.ContainsKey(componentType))
992992
{
993993
return;
994994
}

src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,12 @@ public sealed class TypeDescriptor
6565
internal static readonly object s_commonSyncObject = new object();
6666

6767
// A direct mapping from type to provider.
68-
private static readonly ContextAwareConcurrentHashtable<Type, TypeDescriptionNode> s_providerTypeTable = new ContextAwareConcurrentHashtable<Type, TypeDescriptionNode>();
68+
private static readonly CollectibleKeyConcurrentHashtable<Type, TypeDescriptionNode> s_providerTypeTable = new CollectibleKeyConcurrentHashtable<Type, TypeDescriptionNode>();
6969

7070
// Tracks DefaultTypeDescriptionProviderAttributes.
7171
// A value of `null` indicates initialization is in progress.
7272
// A value of s_initializedDefaultProvider indicates the provider is initialized.
73-
private static readonly ContextAwareConcurrentHashtable<Type, object?> s_defaultProviderInitialized = new ContextAwareConcurrentHashtable<Type, object?>();
73+
private static readonly CollectibleKeyConcurrentHashtable<Type, object?> s_defaultProviderInitialized = new CollectibleKeyConcurrentHashtable<Type, object?>();
7474

7575
private static readonly object s_initializedDefaultProvider = new object();
7676

@@ -361,7 +361,7 @@ private static void AddDefaultProvider(Type type)
361361
{
362362
bool providerAdded = false;
363363

364-
if (s_defaultProviderInitialized.Contains(type))
364+
if (s_defaultProviderInitialized.ContainsKey(type))
365365
{
366366
// Either another thread finished initializing for this type, or we are recursing on the same thread.
367367
return;

src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,6 +1591,8 @@ public static void TypeDescriptor_DoesNotKeepUnloadableTypes()
15911591
Assert.NotNull(collectibleAttributeType);
15921592
Attribute? collectibleAttribute = collectibleType.GetCustomAttribute(collectibleAttributeType);
15931593
Assert.NotNull(collectibleAttribute);
1594+
Type? collectibleProviderType = assembly.GetType("UnloadableTestTypes.SimpleTypeDescriptionProvider");
1595+
Assert.NotNull(collectibleProviderType);
15941596

15951597
// Cache the type's cachable entities
15961598
AttributeCollection attributes = TypeDescriptor.GetAttributes(collectibleType);
@@ -1602,8 +1604,10 @@ public static void TypeDescriptor_DoesNotKeepUnloadableTypes()
16021604
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(collectibleType);
16031605
Assert.Equal(2, properties.Count);
16041606

1607+
// Test that the provider is the expected collectible one
16051608
TypeDescriptionProvider provider = TypeDescriptor.GetProvider(collectibleType);
16061609
Assert.NotNull(provider);
1610+
Assert.IsType(collectibleProviderType, provider);
16071611

16081612
// Refresh the assembly causing the ReflectTypeDescriptionProvider caches to be purged.
16091613
TypeDescriptor.Refresh(assembly);
@@ -1613,14 +1617,19 @@ public static void TypeDescriptor_DoesNotKeepUnloadableTypes()
16131617
Assert.NotSame(attributes, TypeDescriptor.GetAttributes(collectibleType));
16141618
Assert.NotSame(events, TypeDescriptor.GetEvents(collectibleType));
16151619
Assert.NotSame(properties, TypeDescriptor.GetProperties(collectibleType));
1620+
Assert.NotSame(provider, TypeDescriptor.GetProvider(collectibleType));
16161621
},
16171622
out var weakRef);
16181623

1624+
// Force garbage collection to ensure the ALC is unloaded and the types are collected.
16191625
for (int i = 0; weakRef.IsAlive && i < 10; i++)
16201626
{
16211627
GC.Collect();
16221628
GC.WaitForPendingFinalizers();
16231629
}
1630+
1631+
// Assert that the weak reference to the ALC is no longer alive,
1632+
// indicating that the unloaded AssemblyLoadContext has been at least collected.
16241633
Assert.True(!weakRef.IsAlive);
16251634
}
16261635

src/libraries/System.ComponentModel.TypeConverter/tests/UnloadableTestTypes/UnloadableTestTypes.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.ComponentModel;
56

67
namespace UnloadableTestTypes
78
{
89
[SimpleType]
10+
[TypeDescriptionProvider(typeof(SimpleTypeDescriptionProvider))]
911
public class SimpleType
1012
{
1113
public string P1 { get; set; }
@@ -19,4 +21,20 @@ public void OnActionEvent()
1921

2022
[AttributeUsage(AttributeTargets.All)]
2123
public sealed class SimpleTypeAttribute : Attribute { }
24+
25+
public sealed class SimpleTypeDescriptionProvider : TypeDescriptionProvider
26+
{
27+
public SimpleTypeDescriptionProvider() { }
28+
29+
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
30+
{
31+
var baseDescriptor = base.GetTypeDescriptor(objectType, instance);
32+
return new SimpleTypeDescriptor(baseDescriptor);
33+
}
34+
35+
private sealed class SimpleTypeDescriptor : CustomTypeDescriptor
36+
{
37+
public SimpleTypeDescriptor(ICustomTypeDescriptor parent) : base(parent) { }
38+
}
39+
}
2240
}

0 commit comments

Comments
 (0)