diff --git a/src/System.Management.Automation/engine/MshMemberInfo.cs b/src/System.Management.Automation/engine/MshMemberInfo.cs index 35c6acecedd..3b752599a6d 100644 --- a/src/System.Management.Automation/engine/MshMemberInfo.cs +++ b/src/System.Management.Automation/engine/MshMemberInfo.cs @@ -4008,20 +4008,36 @@ public virtual IEnumerator GetEnumerator() /// internal class PSMemberInfoInternalCollection : PSMemberInfoCollection, IEnumerable where T : PSMemberInfo { - private readonly OrderedDictionary _members; + private OrderedDictionary _members; private int _countHidden; + /// + /// Gets the OrderedDictionary for holding all members. + /// We use this property to delay initializing _members until we absolutely need to. + /// + private OrderedDictionary Members + { + get + { + if (_members == null) + { + System.Threading.Interlocked.CompareExchange(ref _members, new OrderedDictionary(StringComparer.OrdinalIgnoreCase), null); + } + + return _members; + } + } + /// /// Constructs this collection /// internal PSMemberInfoInternalCollection() { - _members = new OrderedDictionary(StringComparer.OrdinalIgnoreCase); } private void Replace(T oldMember, T newMember) { - _members[newMember.Name] = newMember; + Members[newMember.Name] = newMember; if (oldMember.IsHidden) { _countHidden--; @@ -4040,9 +4056,11 @@ internal void Replace(T newMember) { Diagnostics.Assert(newMember != null, "called from internal code that checks for new member not null"); - lock (_members) + // Save to a local variable to reduce property access. + var members = Members; + lock (members) { - var oldMember = _members[newMember.Name] as T; + var oldMember = members[newMember.Name] as T; Diagnostics.Assert(oldMember != null, "internal code checks member already exists"); Replace(oldMember, newMember); } @@ -4075,16 +4093,17 @@ public override void Add(T member, bool preValidated) throw PSTraceSource.NewArgumentNullException("member"); } - lock (_members) + // Save to a local variable to reduce property access. + var members = Members; + lock (members) { - var existingMember = _members[member.Name] as T; - if (existingMember != null) + if (members[member.Name] is T existingMember) { Replace(existingMember, member); } else { - _members[member.Name] = member; + members[member.Name] = member; if (member.IsHidden) { _countHidden++; @@ -4114,10 +4133,14 @@ public override void Remove(string name) name); } + if (_members == null) + { + return; + } + lock (_members) { - var member = _members[name] as PSMemberInfo; - if (member != null) + if (_members[name] is PSMemberInfo member) { if (member.IsHidden) { @@ -4143,6 +4166,11 @@ public override T this[string name] throw PSTraceSource.NewArgumentException("name"); } + if (_members == null) + { + return null; + } + lock (_members) { return _members[name] as T; @@ -4203,6 +4231,12 @@ internal override ReadOnlyPSMemberInfoCollection Match(string name, PSMemberT private PSMemberInfoInternalCollection GetInternalMembers(MshMemberMatchOptions matchOptions) { PSMemberInfoInternalCollection returnValue = new PSMemberInfoInternalCollection(); + + if (_members == null) + { + return returnValue; + } + lock (_members) { foreach (T member in _members.Values.OfType()) @@ -4224,6 +4258,11 @@ internal int Count { get { + if (_members == null) + { + return 0; + } + lock (_members) { return _members.Count; @@ -4238,6 +4277,11 @@ internal int VisibleCount { get { + if (_members == null) + { + return 0; + } + lock (_members) { return _members.Count - _countHidden; @@ -4254,6 +4298,11 @@ internal T this[int index] { get { + if (_members == null) + { + return null; + } + lock (_members) { return _members[index] as T; @@ -4269,6 +4318,11 @@ internal T this[int index] /// the enumerator for this collection public override IEnumerator GetEnumerator() { + if (_members == null) + { + return Enumerable.Empty().GetEnumerator(); + } + lock (_members) { // Copy the members to a list so that iteration can be performed without holding a lock.