From 90276100564d05ea0013b20648275e84c585fc04 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Wed, 3 Jul 2024 18:02:25 -0500 Subject: [PATCH 1/7] Use ConcurrentDictionary to avoid threading issues --- ...System.ComponentModel.TypeConverter.csproj | 1 + .../ComponentModel/PropertyDescriptor.cs | 32 +++++++------ .../ReflectTypeDescriptionProvider.cs | 45 +++++++------------ .../System/ComponentModel/TypeDescriptor.cs | 5 ++- 4 files changed, 38 insertions(+), 45 deletions(-) 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 588c6a970ae5ed..6979742610c466 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj @@ -250,6 +250,7 @@ + diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs index 23d05036265ec8..85f98d7bd2141b 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Reflection; @@ -16,10 +17,11 @@ public abstract class PropertyDescriptor : MemberDescriptor internal const string PropertyDescriptorPropertyTypeMessage = "PropertyDescriptor's PropertyType cannot be statically discovered."; private TypeConverter? _converter; - private Dictionary? _valueChangedHandlers; + private ConcurrentDictionary? _valueChangedHandlers; private object?[]? _editors; private Type[]? _editorTypes; private int _editorCount; + private readonly object s_internalSyncObject = new object(); /// /// Initializes a new instance of the class with the specified name and @@ -165,10 +167,11 @@ public virtual void AddValueChanged(object component, EventHandler handler) ArgumentNullException.ThrowIfNull(component); ArgumentNullException.ThrowIfNull(handler); - _valueChangedHandlers ??= new Dictionary(); - - EventHandler? h = _valueChangedHandlers.GetValueOrDefault(component, defaultValue: null); - _valueChangedHandlers[component] = (EventHandler?)Delegate.Combine(h, handler); + lock (s_internalSyncObject) + { + _valueChangedHandlers ??= new ConcurrentDictionary(); + _valueChangedHandlers.AddOrUpdate(component, handler, (k, v) => (EventHandler?)Delegate.Combine(v)); + } } /// @@ -433,15 +436,18 @@ public virtual void RemoveValueChanged(object component, EventHandler handler) if (_valueChangedHandlers != null) { - EventHandler? h = _valueChangedHandlers.GetValueOrDefault(component, defaultValue: null); - h = (EventHandler?)Delegate.Remove(h, handler); - if (h != null) - { - _valueChangedHandlers[component] = h; - } - else + lock (s_internalSyncObject) { - _valueChangedHandlers.Remove(component); + EventHandler? h = _valueChangedHandlers.GetValueOrDefault(component, defaultValue: null); + h = (EventHandler?)Delegate.Remove(h, handler); + if (h != null) + { + _valueChangedHandlers[component] = h; + } + else + { + _valueChangedHandlers.Remove(component, out EventHandler? _); + } } } } 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 531989154e4c6a..5b2f78b5bdc960 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; +using System.Collections.Concurrent; using System.ComponentModel.Design; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -23,7 +24,7 @@ namespace System.ComponentModel internal sealed partial class ReflectTypeDescriptionProvider : TypeDescriptionProvider { // ReflectedTypeData contains all of the type information we have gathered for a given type. - private Dictionary? _typeData; + private readonly ConcurrentDictionary _typeData = new ConcurrentDictionary(); // This is the signature we look for when creating types that are generic, but // want to know what type they are dealing with. Enums are a good example of this; @@ -281,8 +282,7 @@ internal static void AddEditorTable(Type editorBaseType, Hashtable table) public override bool? RequireRegisteredTypes => true; public override bool IsRegisteredType(Type type) { - if (_typeData != null && - _typeData.TryGetValue(type, out ReflectedTypeData? data) && + if (_typeData.TryGetValue(type, out ReflectedTypeData? data) && data.IsRegistered) { return true; @@ -864,15 +864,11 @@ internal Type[] GetPopulatedTypes(Module module) lock (TypeDescriptor.s_commonSyncObject) { - Dictionary? typeData = _typeData; - if (typeData != null) + foreach (KeyValuePair kvp in _typeData) { - foreach (KeyValuePair kvp in typeData) + if (kvp.Key.Module == module && kvp.Value!.IsPopulated) { - if (kvp.Key.Module == module && kvp.Value!.IsPopulated) - { - typeList.Add(kvp.Key); - } + typeList.Add(kvp.Key); } } } @@ -927,9 +923,7 @@ public override Type GetReflectionType( /// private ReflectedTypeData? GetTypeData([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, bool createIfNeeded) { - ReflectedTypeData? td = null; - - if (_typeData != null && _typeData.TryGetValue(type, out td)) + if (_typeData.TryGetValue(type, out ReflectedTypeData? td)) { Debug.Assert(td != null); return td; @@ -937,7 +931,7 @@ public override Type GetReflectionType( lock (TypeDescriptor.s_commonSyncObject) { - if (_typeData != null && _typeData.TryGetValue(type, out td)) + if (_typeData.TryGetValue(type, out td)) { Debug.Assert(td != null); @@ -958,7 +952,6 @@ public override Type GetReflectionType( if (createIfNeeded) { td = new ReflectedTypeData(type, isRegisteredType: false); - _typeData ??= new Dictionary(); _typeData[type] = td; } } @@ -968,7 +961,7 @@ public override Type GetReflectionType( private ReflectedTypeData GetTypeDataFromRegisteredType(Type type) { - if (_typeData == null || !_typeData.TryGetValue(type, out ReflectedTypeData? td)) + if (!_typeData.TryGetValue(type, out ReflectedTypeData? td)) { if (IsIntrinsicType(type)) { @@ -991,41 +984,34 @@ private ReflectedTypeData GetTypeDataFromRegisteredType(Type type) public override void RegisterType<[DynamicallyAccessedMembers(TypeDescriptor.RegisteredTypesDynamicallyAccessedMembers)] T>() { Type componentType = typeof(T); - ReflectedTypeData? td = null; - if (_typeData != null && _typeData.ContainsKey(componentType)) + if (_typeData.ContainsKey(componentType)) { return; } lock (TypeDescriptor.s_commonSyncObject) { - if (_typeData != null && _typeData.ContainsKey(componentType)) + if (_typeData.ContainsKey(componentType)) { return; } - if (td == null) - { - td = new ReflectedTypeData(componentType, isRegisteredType: true); - _typeData ??= new Dictionary(); - _typeData[componentType] = td; - } + ReflectedTypeData td = new ReflectedTypeData(componentType, isRegisteredType: true); + _typeData[componentType] = td; } } private ReflectedTypeData GetOrRegisterType(Type type) { - ReflectedTypeData? td = null; - - if (_typeData != null && _typeData.TryGetValue(type, out td)) + if (_typeData.TryGetValue(type, out ReflectedTypeData? td)) { return td; } lock (TypeDescriptor.s_commonSyncObject) { - if (_typeData != null && _typeData.TryGetValue(type, out td)) + if (_typeData.TryGetValue(type, out td)) { return td; } @@ -1033,7 +1019,6 @@ private ReflectedTypeData GetOrRegisterType(Type type) if (td == null) { td = new ReflectedTypeData(type, isRegisteredType: true); - _typeData ??= new Dictionary(); _typeData[type] = td; } } 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 78f34efbfb73a6..6b782a9fdda039 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel.Design; @@ -56,12 +57,12 @@ public sealed class TypeDescriptor internal static readonly object s_commonSyncObject = new object(); // A direct mapping from type to provider. - private static readonly Dictionary s_providerTypeTable = new Dictionary(); + private static readonly ConcurrentDictionary s_providerTypeTable = new ConcurrentDictionary(); // Tracks DefaultTypeDescriptionProviderAttributes. // A value of `null` indicates initialization is in progress. // A value of s_initializedDefaultProvider indicates the provider is initialized. - private static readonly Dictionary s_defaultProviderInitialized = new Dictionary(); + private static readonly ConcurrentDictionary s_defaultProviderInitialized = new ConcurrentDictionary(); private static readonly object s_initializedDefaultProvider = new object(); From fb391a7e81857ce03889bd8e226ce1d41e5843ca Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Fri, 5 Jul 2024 11:01:12 -0500 Subject: [PATCH 2/7] Use LazyInitializer.EnsureInitialized() --- .../src/System/ComponentModel/PropertyDescriptor.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs index 85f98d7bd2141b..56385930bd3d80 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Threading; namespace System.ComponentModel { @@ -21,7 +22,7 @@ public abstract class PropertyDescriptor : MemberDescriptor private object?[]? _editors; private Type[]? _editorTypes; private int _editorCount; - private readonly object s_internalSyncObject = new object(); + private object? _syncObject; /// /// Initializes a new instance of the class with the specified name and @@ -51,6 +52,8 @@ protected PropertyDescriptor(MemberDescriptor descr, Attribute[]? attrs) : base( { } + private object SyncObject => LazyInitializer.EnsureInitialized(ref _syncObject); + /// /// When overridden in a derived class, gets the type of the /// component this property is bound to. @@ -167,7 +170,7 @@ public virtual void AddValueChanged(object component, EventHandler handler) ArgumentNullException.ThrowIfNull(component); ArgumentNullException.ThrowIfNull(handler); - lock (s_internalSyncObject) + lock (SyncObject) { _valueChangedHandlers ??= new ConcurrentDictionary(); _valueChangedHandlers.AddOrUpdate(component, handler, (k, v) => (EventHandler?)Delegate.Combine(v)); @@ -436,7 +439,7 @@ public virtual void RemoveValueChanged(object component, EventHandler handler) if (_valueChangedHandlers != null) { - lock (s_internalSyncObject) + lock (SyncObject) { EventHandler? h = _valueChangedHandlers.GetValueOrDefault(component, defaultValue: null); h = (EventHandler?)Delegate.Remove(h, handler); From 53e140d69e947a3008fecb4c33ce3bb86050320b Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Fri, 5 Jul 2024 13:24:10 -0500 Subject: [PATCH 3/7] Add test and fix issue with AddValueChanged --- .../ComponentModel/PropertyDescriptor.cs | 2 +- .../tests/PropertyDescriptorTests.cs | 38 ++++++++++++++----- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs index 56385930bd3d80..81a72fa692903b 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs @@ -173,7 +173,7 @@ public virtual void AddValueChanged(object component, EventHandler handler) lock (SyncObject) { _valueChangedHandlers ??= new ConcurrentDictionary(); - _valueChangedHandlers.AddOrUpdate(component, handler, (k, v) => (EventHandler?)Delegate.Combine(v)); + _valueChangedHandlers.AddOrUpdate(component, handler, (k, v) => (EventHandler?)Delegate.Combine(v, handler)); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/PropertyDescriptorTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/PropertyDescriptorTests.cs index f791d46824f829..4786d3966a7e18 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/PropertyDescriptorTests.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/PropertyDescriptorTests.cs @@ -28,13 +28,21 @@ public void RaiseAddedValueChangedHandler() var component = new DescriptorTestComponent(); var properties = TypeDescriptor.GetProperties(component.GetType()); PropertyDescriptor propertyDescriptor = properties.Find(nameof(component.Property), false); - var handlerWasCalled = false; - EventHandler valueChangedHandler = (_, __) => handlerWasCalled = true; + int handlerCalledCount = 0; - propertyDescriptor.AddValueChanged(component, valueChangedHandler); - propertyDescriptor.SetValue(component, int.MaxValue); + EventHandler valueChangedHandler1 = (_, __) => handlerCalledCount++; + EventHandler valueChangedHandler2 = (_, __) => handlerCalledCount++; + + propertyDescriptor.AddValueChanged(component, valueChangedHandler1); + + // Add case. + propertyDescriptor.SetValue(component, int.MaxValue); // Add to delegate. + Assert.Equal(1, handlerCalledCount); - Assert.True(handlerWasCalled); + + propertyDescriptor.AddValueChanged(component, valueChangedHandler2); + propertyDescriptor.SetValue(component, int.MaxValue); // Update delegate. + Assert.Equal(3, handlerCalledCount); } [Fact] @@ -42,15 +50,25 @@ public void RemoveAddedValueChangedHandler() { var component = new DescriptorTestComponent(); var properties = TypeDescriptor.GetProperties(component.GetType()); - var handlerWasCalled = false; - EventHandler valueChangedHandler = (_, __) => handlerWasCalled = true; + int handlerCalledCount = 0; + + EventHandler valueChangedHandler1 = (_, __) => handlerCalledCount++; + EventHandler valueChangedHandler2 = (_, __) => handlerCalledCount++; + PropertyDescriptor propertyDescriptor = properties.Find(nameof(component.Property), false); - propertyDescriptor.AddValueChanged(component, valueChangedHandler); - propertyDescriptor.RemoveValueChanged(component, valueChangedHandler); + propertyDescriptor.AddValueChanged(component, valueChangedHandler1); + propertyDescriptor.AddValueChanged(component, valueChangedHandler2); + propertyDescriptor.SetValue(component, int.MaxValue); + Assert.Equal(2, handlerCalledCount); + propertyDescriptor.SetValue(component, int.MaxValue); + Assert.Equal(4, handlerCalledCount); - Assert.False(handlerWasCalled); + propertyDescriptor.RemoveValueChanged(component, valueChangedHandler1); + propertyDescriptor.RemoveValueChanged(component, valueChangedHandler2); + propertyDescriptor.SetValue(component, int.MaxValue); + Assert.Equal(4, handlerCalledCount); } [Fact] From 8fc271912c927fe444ede629e1a5c80258bcc2d0 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Fri, 5 Jul 2024 13:32:41 -0500 Subject: [PATCH 4/7] Use Interlocked.CompareExchange instead of a lock for Update --- .../src/System/ComponentModel/PropertyDescriptor.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs index 81a72fa692903b..edf92e78796ca3 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs @@ -170,11 +170,12 @@ public virtual void AddValueChanged(object component, EventHandler handler) ArgumentNullException.ThrowIfNull(component); ArgumentNullException.ThrowIfNull(handler); - lock (SyncObject) + if (_valueChangedHandlers is null) { - _valueChangedHandlers ??= new ConcurrentDictionary(); - _valueChangedHandlers.AddOrUpdate(component, handler, (k, v) => (EventHandler?)Delegate.Combine(v, handler)); + Interlocked.CompareExchange(ref _valueChangedHandlers, new ConcurrentDictionary(), null); } + + _valueChangedHandlers.AddOrUpdate(component, handler, (k, v) => (EventHandler?)Delegate.Combine(v, handler)); } /// From 570ad78f15ed5a7051f301cd88f4e1902a56e531 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Fri, 5 Jul 2024 14:09:56 -0500 Subject: [PATCH 5/7] Revert "Use Interlocked.CompareExchange instead of a lock for Update" This reverts commit 8fc271912c927fe444ede629e1a5c80258bcc2d0. --- .../src/System/ComponentModel/PropertyDescriptor.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs index edf92e78796ca3..81a72fa692903b 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs @@ -170,12 +170,11 @@ public virtual void AddValueChanged(object component, EventHandler handler) ArgumentNullException.ThrowIfNull(component); ArgumentNullException.ThrowIfNull(handler); - if (_valueChangedHandlers is null) + lock (SyncObject) { - Interlocked.CompareExchange(ref _valueChangedHandlers, new ConcurrentDictionary(), null); + _valueChangedHandlers ??= new ConcurrentDictionary(); + _valueChangedHandlers.AddOrUpdate(component, handler, (k, v) => (EventHandler?)Delegate.Combine(v, handler)); } - - _valueChangedHandlers.AddOrUpdate(component, handler, (k, v) => (EventHandler?)Delegate.Combine(v, handler)); } /// From 791aac2ee594d68b8dbd6c6c52760b14a19c9e57 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Mon, 8 Jul 2024 08:13:19 -0500 Subject: [PATCH 6/7] Set concurrencyLevel=1 on dictionary --- .../src/System/ComponentModel/PropertyDescriptor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs index 81a72fa692903b..ea9a7f1ac42d38 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs @@ -172,7 +172,7 @@ public virtual void AddValueChanged(object component, EventHandler handler) lock (SyncObject) { - _valueChangedHandlers ??= new ConcurrentDictionary(); + _valueChangedHandlers ??= new ConcurrentDictionary(concurrencyLevel: 1, capacity: -1); _valueChangedHandlers.AddOrUpdate(component, handler, (k, v) => (EventHandler?)Delegate.Combine(v, handler)); } } From ae213ae4bfd7be0e078d4bdebeed4ed8a96ab70e Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Mon, 8 Jul 2024 08:24:46 -0500 Subject: [PATCH 7/7] Remove unnecessary lock due to use of ConcurrentDictionary; change capacity from -1 to 0 --- .../src/System/ComponentModel/PropertyDescriptor.cs | 2 +- .../ComponentModel/ReflectTypeDescriptionProvider.cs | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs index ea9a7f1ac42d38..30d6a262e31db2 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs @@ -172,7 +172,7 @@ public virtual void AddValueChanged(object component, EventHandler handler) lock (SyncObject) { - _valueChangedHandlers ??= new ConcurrentDictionary(concurrencyLevel: 1, capacity: -1); + _valueChangedHandlers ??= new ConcurrentDictionary(concurrencyLevel: 1, capacity: 0); _valueChangedHandlers.AddOrUpdate(component, handler, (k, v) => (EventHandler?)Delegate.Combine(v, handler)); } } 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 5b2f78b5bdc960..d7c74e719d36ce 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs @@ -862,14 +862,11 @@ internal Type[] GetPopulatedTypes(Module module) { List typeList = new List(); - lock (TypeDescriptor.s_commonSyncObject) + foreach (KeyValuePair kvp in _typeData) { - foreach (KeyValuePair kvp in _typeData) + if (kvp.Key.Module == module && kvp.Value!.IsPopulated) { - if (kvp.Key.Module == module && kvp.Value!.IsPopulated) - { - typeList.Add(kvp.Key); - } + typeList.Add(kvp.Key); } }