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