1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System.Diagnostics;
6 using System.Collections.Generic;
8 namespace System.Reflection.Runtime.BindingFlagSupport
11 // Stores the result of a member filtering that's further filtered by the Public, NonPublic, Instance, Static and FlatternHierarchy bits of BindingFlags.
12 // This object is not considered a candidate for long term caching.
14 // Note: The uninitialized state ("qr = default(QueryResult<M>)) is considered a valid state for this object, and represents an empty list of members.
16 internal partial struct QueryResult<M> where M : MemberInfo
18 public QueryResult(BindingFlags bindingAttr, QueriedMemberList<M> queriedMembers)
21 _bindingAttr = bindingAttr;
22 _queriedMembers = queriedMembers;
25 public QueryResultEnumerator GetEnumerator() => new QueryResultEnumerator(this);
28 /// Returns the number of matching results.
34 int count = _lazyCount;
37 if (_queriedMembers == null)
38 return 0; // This is an uninitialized QueryResult<M>, which is supported and represents a 0-length list of matches.
40 int unfilteredCount = UnfilteredCount;
41 for (int i = 0; i < unfilteredCount; i++)
43 if (_queriedMembers.Matches(i, _bindingAttr))
49 // If no matches were found, set ourselves back to the "uninitialized" state so that future
50 // calls to Count won't go through this calculation again.
51 _queriedMembers = null;
62 /// Copies the results to a freshly allocated array. Use this at api boundary points.
68 return Array.Empty<M>();
70 M[] newArray = new M[count];
76 /// Copies the results into an existing array.
78 public void CopyTo(MemberInfo[] array, int startIndex)
80 if (_queriedMembers == null)
81 return; // This is an uninitialized QueryResult<M>, which is supported and represents a 0-length list of matches.
83 int unfilteredCount = UnfilteredCount;
84 for (int i = 0; i < unfilteredCount; i++)
86 if (_queriedMembers.Matches(i, _bindingAttr))
88 array[startIndex++] = _queriedMembers[i];
94 /// Returns a single member, null or throws AmbigousMatchException, for the Type.Get*(string name,...) family of apis.
96 public M Disambiguate()
98 if (_queriedMembers == null)
99 return null; // This is an uninitialized QueryResult<M>, which is supported and represents a 0-length list of matches.
101 int unfilteredCount = UnfilteredCount;
104 for (int i = 0; i < unfilteredCount; i++)
106 if (_queriedMembers.Matches(i, _bindingAttr))
110 M challenger = _queriedMembers[i];
112 // Assuming the policy says it's ok to ignore the ambiguity, we're to resolve in favor of the member
113 // declared by the most derived type. Since QueriedMemberLists are sorted in order of decreasing derivation,
114 // that means we let the first match win - unless, of course, they're both the "most derived member".
115 if (match.DeclaringType.Equals(challenger.DeclaringType))
116 throw new AmbiguousMatchException();
118 MemberPolicies<M> policies = MemberPolicies<M>.Default;
119 if (!policies.OkToIgnoreAmbiguity(match, challenger))
120 throw new AmbiguousMatchException();
124 match = _queriedMembers[i];
131 private int UnfilteredCount => ((_bindingAttr & BindingFlags.DeclaredOnly) != 0) ? _queriedMembers.DeclaredOnlyCount : _queriedMembers.TotalCount;
133 private readonly BindingFlags _bindingAttr;
134 private int _lazyCount; // Intentionally not marking as volatile. QueryResult is for short-term use within a single method call - no aspiration to be thread-safe.
135 private QueriedMemberList<M> _queriedMembers;