b3519b958a30a54948932db13af912af98830cfa
[platform/upstream/dotnet/runtime.git] /
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.
4
5 using System.Diagnostics;
6 using System.Collections.Generic;
7
8 namespace System.Reflection.Runtime.BindingFlagSupport
9 {
10     //
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.
13     //
14     // Note: The uninitialized state ("qr = default(QueryResult<M>)) is considered a valid state for this object, and represents an empty list of members.
15     //
16     internal partial struct QueryResult<M> where M : MemberInfo
17     {
18         public QueryResult(BindingFlags bindingAttr, QueriedMemberList<M> queriedMembers)
19         {
20             _lazyCount = 0;
21             _bindingAttr = bindingAttr;
22             _queriedMembers = queriedMembers;
23         }
24
25         public QueryResultEnumerator GetEnumerator() => new QueryResultEnumerator(this);
26
27         /// <summary>
28         /// Returns the number of matching results.
29         /// </summary>
30         public int Count
31         {
32             get
33             {
34                 int count = _lazyCount;
35                 if (count == 0)
36                 {
37                     if (_queriedMembers == null)
38                         return 0;  // This is an uninitialized QueryResult<M>, which is supported and represents a 0-length list of matches.
39
40                     int unfilteredCount = UnfilteredCount;
41                     for (int i = 0; i < unfilteredCount; i++)
42                     {
43                         if (_queriedMembers.Matches(i, _bindingAttr))
44                             count++;
45                     }
46
47                     if (count == 0)
48                     {
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;
52                     }
53
54                     _lazyCount = count;
55
56                 }
57                 return count;
58             }
59         }
60
61         /// <summary>
62         /// Copies the results to a freshly allocated array. Use this at api boundary points.
63         /// </summary>
64         public M[] ToArray()
65         {
66             int count = Count;
67             if (count == 0)
68                 return Array.Empty<M>();
69
70             M[] newArray = new M[count];
71             CopyTo(newArray, 0);
72             return newArray;
73         }
74
75         /// <summary>
76         /// Copies the results into an existing array.
77         /// </summary>
78         public void CopyTo(MemberInfo[] array, int startIndex)
79         {
80             if (_queriedMembers == null)
81                 return; // This is an uninitialized QueryResult<M>, which is supported and represents a 0-length list of matches.
82
83             int unfilteredCount = UnfilteredCount;
84             for (int i = 0; i < unfilteredCount; i++)
85             {
86                 if (_queriedMembers.Matches(i, _bindingAttr))
87                 {
88                     array[startIndex++] = _queriedMembers[i];
89                 }
90             }
91         }
92
93         /// <summary>
94         /// Returns a single member, null or throws AmbigousMatchException, for the Type.Get*(string name,...) family of apis.
95         /// </summary>
96         public M Disambiguate()
97         {
98             if (_queriedMembers == null)
99                 return null; // This is an uninitialized QueryResult<M>, which is supported and represents a 0-length list of matches.
100
101             int unfilteredCount = UnfilteredCount;
102
103             M match = null;
104             for (int i = 0; i < unfilteredCount; i++)
105             {
106                 if (_queriedMembers.Matches(i, _bindingAttr))
107                 {
108                     if (match != null)
109                     {
110                         M challenger = _queriedMembers[i];
111
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();
117
118                         MemberPolicies<M> policies = MemberPolicies<M>.Default;
119                         if (!policies.OkToIgnoreAmbiguity(match, challenger))
120                             throw new AmbiguousMatchException();
121                     }
122                     else
123                     {
124                         match = _queriedMembers[i];
125                     }
126                 }
127             }
128             return match;
129         }
130
131         private int UnfilteredCount => ((_bindingAttr & BindingFlags.DeclaredOnly) != 0) ? _queriedMembers.DeclaredOnlyCount : _queriedMembers.TotalCount;
132
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;
136     }
137 }