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;
6 using System.Reflection;
7 using System.Diagnostics;
8 using System.Collections;
9 using System.Collections.Generic;
10 using System.Collections.ObjectModel;
11 using System.Runtime.CompilerServices;
12 using Internal.Reflection.Extensions.NonPortable;
13 
14 using Internal.LowLevelLinq;
15 
16 namespace System.Reflection
17 {
18     //==============================================================================================================
19     // This api set retrieves the "effective" set of custom attributes associated with a given Reflection element.
20     // The effective set not only includes attributes declared directly on the element but attributes inherited
21     // from the element's "parents."
22     //
23     // Api conventions:
24     //
25     //    - "T or "attributeType" arguments must be non-null, non-interface type that derives from System.Attribute.
26     //
27     //    - The default for the "inherited" parameter is "true". For Assemblies, Modules and FieldInfos,
28     //      the api ignores the value of "inherited."
29     //
30     //
31     // Definition of "effective" set of custom attributes:
32     //
33     //   The following element types can inherit custom attributes from its "parents". Inheritance is transitive,
34     //   so this also includes grandparents, etc.
35     //
36     //      - TypeInfos inherit from base classes (but not interfaces.)
37     //
38     //      - MethodInfos that override a virtual in a base class (but not an interface) inherit
39     //        from the method it overrode.
40     //
41     //      - PropertyInfos that override a virtual in a base class (but not an interface) inherit
42     //        from the property it override.
43     //
44     //      - EventInfos that override a virtual in a base class (but not an interface) inherit
45     //        from the event it override.
46     //
47     //      - ParameterInfos whose declaring method overrides a virtual in a base class (but not an interface)
48     //        inherit from the matching parameter in the method that was overridden.
49     //
50     //   Custom attributes only flow down this chain if they are marked inheritable. Note that the
51     //   AttributeUsageAttribute attribute it itself inheritable, and custom attributes can derive from other custom attributes:
52     //   if a custom attribute and its base class(s) both define AttributeUsages, the most derived AttributeUsage wins.
53     //
54     //   If an element and one of its parents both include a custom attribute of the *exact same type* (even
55     //   if calling different overloads of the constructor), and that attribute types does *not* declare AllowMultiple=true,
56     //   these apis will only return the one attached to the most derived parent.)
57     //
58     // Dependency note:
59     //   This class must depend only on the CustomAttribute properties that return IEnumerable<CustomAttributeData>.
60     //   All of the other custom attribute api route back here so calls to them will cause an infinite recursion.
61     //
62     //==============================================================================================================
63 
64     public static class CustomAttributeExtensions
65     {
66         //==============================================================================================================
67         // This group returns the single custom attribute whose type matches or is a subclass of "attributeType".
68         //
69         // Returns null if no matching custom attribute found.
70         //
71         // Throws AmbiguousMatchException if multple matches found.
72         //==============================================================================================================
GetCustomAttribute(this Assembly element, Type attributeType)73         public static Attribute GetCustomAttribute(this Assembly element, Type attributeType)
74         {
75             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType);
76             return matches.OneOrNull<Attribute>();
77         }
78 
GetCustomAttribute(this Module element, Type attributeType)79         public static Attribute GetCustomAttribute(this Module element, Type attributeType)
80         {
81             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType);
82             return matches.OneOrNull<Attribute>();
83         }
84 
GetCustomAttribute(this MemberInfo element, Type attributeType)85         public static Attribute GetCustomAttribute(this MemberInfo element, Type attributeType)
86         {
87             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType, inherit: true);
88             return matches.OneOrNull<Attribute>();
89         }
90 
GetCustomAttribute(this MemberInfo element, Type attributeType, bool inherit)91         public static Attribute GetCustomAttribute(this MemberInfo element, Type attributeType, bool inherit)
92         {
93             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType, inherit);
94             return matches.OneOrNull<Attribute>();
95         }
96 
GetCustomAttribute(this ParameterInfo element, Type attributeType)97         public static Attribute GetCustomAttribute(this ParameterInfo element, Type attributeType)
98         {
99             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType, inherit: true);
100             return matches.OneOrNull<Attribute>();
101         }
102 
GetCustomAttribute(this ParameterInfo element, Type attributeType, bool inherit)103         public static Attribute GetCustomAttribute(this ParameterInfo element, Type attributeType, bool inherit)
104         {
105             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType, inherit);
106             return matches.OneOrNull<Attribute>();
107         }
108 
109 
110 
111         //==============================================================================================================
112         // This group returns the single custom attribute whose type matches or is a subclass of "T".
113         //
114         // Returns null if no matching custom attribute found.
115         //
116         // Throws AmbiguousMatchException if multple matches found.
117         //==============================================================================================================
118         public static T GetCustomAttribute<T>(this Assembly element) where T : Attribute
119         {
120             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(typeof(T), skipTypeValidation: true);
121             return matches.OneOrNull<T>();
122         }
123 
124         public static T GetCustomAttribute<T>(this Module element) where T : Attribute
125         {
126             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(typeof(T), skipTypeValidation: true);
127             return matches.OneOrNull<T>();
128         }
129 
130         public static T GetCustomAttribute<T>(this MemberInfo element) where T : Attribute
131         {
132             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(typeof(T), inherit: true, skipTypeValidation: true);
133             return matches.OneOrNull<T>();
134         }
135 
136         public static T GetCustomAttribute<T>(this MemberInfo element, bool inherit) where T : Attribute
137         {
138             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(typeof(T), inherit, skipTypeValidation: true);
139             return matches.OneOrNull<T>();
140         }
141 
142         public static T GetCustomAttribute<T>(this ParameterInfo element) where T : Attribute
143         {
144             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(typeof(T), inherit: true, skipTypeValidation: true);
145             return matches.OneOrNull<T>();
146         }
147 
148         public static T GetCustomAttribute<T>(this ParameterInfo element, bool inherit) where T : Attribute
149         {
150             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(typeof(T), inherit, skipTypeValidation: true);
151             return matches.OneOrNull<T>();
152         }
153 
154 
155 
156         //==============================================================================================================
157         // This group retrieves all custom attributes that applies to a given element.
158         //==============================================================================================================
GetCustomAttributes(this Assembly element)159         public static IEnumerable<Attribute> GetCustomAttributes(this Assembly element)
160         {
161             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(null, skipTypeValidation: true);
162             return matches.Select(m => m.Instantiate());
163         }
164 
GetCustomAttributes(this Module element)165         public static IEnumerable<Attribute> GetCustomAttributes(this Module element)
166         {
167             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(null, skipTypeValidation: true);
168             return matches.Select(m => m.Instantiate());
169         }
170 
GetCustomAttributes(this MemberInfo element)171         public static IEnumerable<Attribute> GetCustomAttributes(this MemberInfo element)
172         {
173             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(null, inherit: true, skipTypeValidation: true);
174             return matches.Select(m => m.Instantiate());
175         }
176 
GetCustomAttributes(this MemberInfo element, bool inherit)177         public static IEnumerable<Attribute> GetCustomAttributes(this MemberInfo element, bool inherit)
178         {
179             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(null, inherit, skipTypeValidation: true);
180             return matches.Select(m => m.Instantiate());
181         }
182 
GetCustomAttributes(this ParameterInfo element)183         public static IEnumerable<Attribute> GetCustomAttributes(this ParameterInfo element)
184         {
185             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(null, inherit: true, skipTypeValidation: true);
186             return matches.Select(m => m.Instantiate());
187         }
188 
GetCustomAttributes(this ParameterInfo element, bool inherit)189         public static IEnumerable<Attribute> GetCustomAttributes(this ParameterInfo element, bool inherit)
190         {
191             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(null, inherit, skipTypeValidation: true);
192             return matches.Select(m => m.Instantiate());
193         }
194 
195 
196         //==============================================================================================================
197         // This group retrieves all custom attributes associated with a given element whose attribute type matches or
198         // is a subclass of "attributeType".
199         //==============================================================================================================
GetCustomAttributes(this Assembly element, Type attributeType)200         public static IEnumerable<Attribute> GetCustomAttributes(this Assembly element, Type attributeType)
201         {
202             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType);
203             return matches.Instantiate(attributeType);
204         }
205 
GetCustomAttributes(this Module element, Type attributeType)206         public static IEnumerable<Attribute> GetCustomAttributes(this Module element, Type attributeType)
207         {
208             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType);
209             return matches.Instantiate(attributeType);
210         }
211 
GetCustomAttributes(this MemberInfo element, Type attributeType)212         public static IEnumerable<Attribute> GetCustomAttributes(this MemberInfo element, Type attributeType)
213         {
214             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType, inherit: true);
215             return matches.Instantiate(attributeType);
216         }
217 
GetCustomAttributes(this MemberInfo element, Type attributeType, bool inherit)218         public static IEnumerable<Attribute> GetCustomAttributes(this MemberInfo element, Type attributeType, bool inherit)
219         {
220             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType, inherit);
221             return matches.Instantiate(attributeType);
222         }
223 
GetCustomAttributes(this ParameterInfo element, Type attributeType)224         public static IEnumerable<Attribute> GetCustomAttributes(this ParameterInfo element, Type attributeType)
225         {
226             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType, inherit: true);
227             return matches.Instantiate(attributeType);
228         }
229 
GetCustomAttributes(this ParameterInfo element, Type attributeType, bool inherit)230         public static IEnumerable<Attribute> GetCustomAttributes(this ParameterInfo element, Type attributeType, bool inherit)
231         {
232             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType, inherit);
233             return matches.Instantiate(attributeType);
234         }
235 
236         //==============================================================================================================
237         // This group retrieves all custom attributes associated with a given element whose attribute type matches or
238         // is a subclass of "T".
239         //==============================================================================================================
240         public static IEnumerable<T> GetCustomAttributes<T>(this Assembly element) where T : Attribute
241         {
242             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(typeof(T), skipTypeValidation: true);
243             return matches.Select(m => (T)(m.Instantiate()));
244         }
245 
246         public static IEnumerable<T> GetCustomAttributes<T>(this Module element) where T : Attribute
247         {
248             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(typeof(T), skipTypeValidation: true);
249             return matches.Select(m => (T)(m.Instantiate()));
250         }
251 
252         public static IEnumerable<T> GetCustomAttributes<T>(this MemberInfo element) where T : Attribute
253         {
254             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(typeof(T), inherit: true, skipTypeValidation: true);
255             return matches.Select(m => (T)(m.Instantiate()));
256         }
257 
258         public static IEnumerable<T> GetCustomAttributes<T>(this MemberInfo element, bool inherit) where T : Attribute
259         {
260             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(typeof(T), inherit, skipTypeValidation: true);
261             return matches.Select(m => (T)(m.Instantiate()));
262         }
263 
264         public static IEnumerable<T> GetCustomAttributes<T>(this ParameterInfo element) where T : Attribute
265         {
266             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(typeof(T), inherit: true, skipTypeValidation: true);
267             return matches.Select(m => (T)(m.Instantiate()));
268         }
269 
270         public static IEnumerable<T> GetCustomAttributes<T>(this ParameterInfo element, bool inherit) where T : Attribute
271         {
272             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(typeof(T), inherit, skipTypeValidation: true);
273             return matches.Select(m => (T)(m.Instantiate()));
274         }
275 
276         //==============================================================================================================
277         // This group determines whether the element has an associated custom attribute whose type matches or is a subclass
278         // of "attributeType".
279         //==============================================================================================================
IsDefined(this Assembly element, Type attributeType)280         public static bool IsDefined(this Assembly element, Type attributeType)
281         {
282             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType);
283             return matches.Any();
284         }
285 
IsDefined(this Module element, Type attributeType)286         public static bool IsDefined(this Module element, Type attributeType)
287         {
288             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType);
289             return matches.Any();
290         }
291 
IsDefined(this MemberInfo element, Type attributeType)292         public static bool IsDefined(this MemberInfo element, Type attributeType)
293         {
294             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType, inherit: true);
295             return matches.Any();
296         }
297 
IsDefined(this MemberInfo element, Type attributeType, bool inherit)298         public static bool IsDefined(this MemberInfo element, Type attributeType, bool inherit)
299         {
300             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType, inherit);
301             return matches.Any();
302         }
303 
IsDefined(this ParameterInfo element, Type attributeType)304         public static bool IsDefined(this ParameterInfo element, Type attributeType)
305         {
306             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType, inherit: true);
307             return matches.Any();
308         }
309 
IsDefined(this ParameterInfo element, Type attributeType, bool inherit)310         public static bool IsDefined(this ParameterInfo element, Type attributeType, bool inherit)
311         {
312             IEnumerable<CustomAttributeData> matches = element.GetMatchingCustomAttributes(attributeType, inherit);
313             return matches.Any();
314         }
315 
316 
317         //==============================================================================================================================
318         // Helper for the GetCustomAttribute() family.
319         //==============================================================================================================================
320         private static T OneOrNull<T>(this IEnumerable<CustomAttributeData> results) where T : Attribute
321         {
322             IEnumerator<CustomAttributeData> enumerator = results.GetEnumerator();
323             if (!enumerator.MoveNext())
324                 return null;
325             CustomAttributeData result = enumerator.Current;
326             if (enumerator.MoveNext())
327                 throw new AmbiguousMatchException();
328             return (T)(result.Instantiate());
329         }
330 
331 
332         //==============================================================================================================================
333         // Helper for the GetCustomAttributes() methods that take a specific attribute type. For desktop compatibility,
334         // we return a freshly allocated array of the specific attribute type even though the api's return type promises only an IEnumerable<Attribute>.
335         // There are known store apps that cast the results of apis and expect the cast to work. The implementation of Attribute.GetCustomAttribute()
336         // also relies on this (it performs an unsafe cast to Attribute[] and does not re-copy the array.)
337         //==============================================================================================================================
Instantiate(this IEnumerable<CustomAttributeData> cads, Type actualElementType)338         private static IEnumerable<Attribute> Instantiate(this IEnumerable<CustomAttributeData> cads, Type actualElementType)
339         {
340             LowLevelList<Attribute> attributes = new LowLevelList<Attribute>();
341             foreach (CustomAttributeData cad in cads)
342             {
343                 Attribute instantiatedAttribute = cad.Instantiate();
344                 attributes.Add(instantiatedAttribute);
345             }
346             int count = attributes.Count;
347             Attribute[] result;
348             result = (Attribute[])Array.CreateInstance(actualElementType, count);
349             attributes.CopyTo(result, 0);
350             return result;
351         }
352 
353         //==============================================================================================================================
354         // This is used to "convert" the output of CustomAttributeExtensions.GetCustomAttributes() from IEnumerable<Attribute> to Attribute[]
355         // as required by the Attribute.GetCustomAttributes() members.
356         //
357         // This relies on the fact that CustomAttributeExtensions.GetCustomAttribute()'s actual return type is an array whose element type is that
358         // of the specific attributeType searched on. (Though this isn't explicitly promised, real world code does in fact rely on this so
359         // this is a compat thing we're stuck with now.)
360         //==============================================================================================================================
361         [MethodImpl(MethodImplOptions.AggressiveInlining)]
AsAttributeArray(this IEnumerable<Attribute> attributes)362         internal static Attribute[] AsAttributeArray(this IEnumerable<Attribute> attributes) => (Attribute[])attributes;
363     }
364 }
365