2 // MonoCustomAttrs.cs: Hooks into the runtime to get custom attributes for reflection handles
5 // Paolo Molaro (lupus@ximian.com)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
9 // (c) 2002,2003 Ximian, Inc. (http://www.ximian.com)
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
11 // Copyright (C) 2013 Xamarin, Inc (http://www.xamarin.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System
.Reflection
;
35 using System
.Collections
;
36 using System
.Runtime
.CompilerServices
;
37 using System
.Runtime
.InteropServices
;
39 using System
.Reflection
.Emit
;
42 using System
.Collections
.Generic
;
46 static class MonoCustomAttrs
48 static Assembly corlib
;
50 static Dictionary
<Type
, AttributeUsageAttribute
> usage_cache
;
52 /* Treat as user types all corlib types extending System.Type that are not RuntimeType and TypeBuilder */
53 static bool IsUserCattrProvider (object obj
)
55 Type type
= obj
as Type
;
57 if ((type
is RuntimeType
) || (RuntimeFeature
.IsDynamicCodeSupported
&& type
is TypeBuilder
))
59 if (type
is RuntimeType
)
65 corlib
= typeof (int).Assembly
;
66 return obj
.GetType ().Assembly
!= corlib
;
69 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
70 internal static extern Attribute
[] GetCustomAttributesInternal (ICustomAttributeProvider obj
, Type attributeType
, bool pseudoAttrs
);
72 internal static object[] GetPseudoCustomAttributes (ICustomAttributeProvider obj
, Type attributeType
) {
73 object[] pseudoAttrs
= null;
74 /* FIXME: Add other types */
75 if (obj
is RuntimeMethodInfo monoMethod
)
76 pseudoAttrs
= monoMethod
.GetPseudoCustomAttributes ();
77 else if (obj
is RuntimeFieldInfo fieldInfo
)
78 pseudoAttrs
= fieldInfo
.GetPseudoCustomAttributes ();
79 else if (obj
is RuntimeParameterInfo monoParamInfo
)
80 pseudoAttrs
= monoParamInfo
.GetPseudoCustomAttributes ();
81 else if (obj
is Type t
)
82 pseudoAttrs
= GetPseudoCustomAttributes (t
);
84 if ((attributeType
!= null) && (pseudoAttrs
!= null)) {
85 for (int i
= 0; i
< pseudoAttrs
.Length
; ++i
)
86 if (attributeType
.IsAssignableFrom (pseudoAttrs
[i
].GetType ()))
87 if (pseudoAttrs
.Length
== 1)
90 return new object [] { pseudoAttrs [i] }
;
91 return Array
.Empty
<object> ();
97 static object[] GetPseudoCustomAttributes (Type type
)
100 var Attributes
= type
.Attributes
;
102 /* IsSerializable returns true for delegates/enums as well */
103 if ((Attributes
& TypeAttributes
.Serializable
) != 0)
105 if ((Attributes
& TypeAttributes
.Import
) != 0)
110 object[] attrs
= new object [count
];
113 if ((Attributes
& TypeAttributes
.Serializable
) != 0)
114 attrs
[count
++] = new SerializableAttribute ();
115 if ((Attributes
& TypeAttributes
.Import
) != 0)
116 attrs
[count
++] = new ComImportAttribute ();
121 internal static object[] GetCustomAttributesBase (ICustomAttributeProvider obj
, Type attributeType
, bool inheritedOnly
)
125 if (IsUserCattrProvider (obj
))
126 attrs
= obj
.GetCustomAttributes (attributeType
, true);
128 attrs
= GetCustomAttributesInternal (obj
, attributeType
, false);
131 // All pseudo custom attributes are Inherited = false hence we can avoid
132 // building attributes array which would be discarded by inherited checks
134 if (!inheritedOnly
) {
135 object[] pseudoAttrs
= GetPseudoCustomAttributes (obj
, attributeType
);
136 if (pseudoAttrs
!= null) {
137 object[] res
= new Attribute
[attrs
.Length
+ pseudoAttrs
.Length
];
138 System
.Array
.Copy (attrs
, res
, attrs
.Length
);
139 System
.Array
.Copy (pseudoAttrs
, 0, res
, attrs
.Length
, pseudoAttrs
.Length
);
147 internal static object[] GetCustomAttributes (ICustomAttributeProvider obj
, Type attributeType
, bool inherit
)
150 throw new ArgumentNullException (nameof (obj
));
151 if (attributeType
== null)
152 throw new ArgumentNullException (nameof (attributeType
));
154 if (attributeType
== typeof (MonoCustomAttrs
))
155 attributeType
= null;
158 object[] res
= GetCustomAttributesBase (obj
, attributeType
, false);
160 if (!inherit
&& res
.Length
== 1) {
162 throw new CustomAttributeFormatException ("Invalid custom attribute format");
164 if (attributeType
!= null) {
165 if (attributeType
.IsAssignableFrom (res
[0].GetType ())) {
166 r
= (object[]) Array
.CreateInstance (attributeType
, 1);
169 r
= (object[]) Array
.CreateInstance (attributeType
, 0);
172 r
= (object[]) Array
.CreateInstance (res
[0].GetType (), 1);
178 if (inherit
&& GetBase (obj
) == null)
181 // if AttributeType is sealed, and Inherited is set to false, then
182 // there's no use in scanning base types
183 if ((attributeType
!= null && attributeType
.IsSealed
) && inherit
) {
184 AttributeUsageAttribute usageAttribute
= RetrieveAttributeUsage (
186 if (!usageAttribute
.Inherited
)
190 var initialSize
= Math
.Max (res
.Length
, 16);
191 List
<Object
> a
= null;
192 ICustomAttributeProvider btype
= obj
;
195 /* Non-inherit case */
197 if (attributeType
== null) {
198 foreach (object attr
in res
) {
200 throw new CustomAttributeFormatException ("Invalid custom attribute format");
202 var result
= new Attribute
[res
.Length
];
203 res
.CopyTo (result
, 0);
207 a
= new List
<object> (initialSize
);
208 foreach (object attr
in res
) {
210 throw new CustomAttributeFormatException ("Invalid custom attribute format");
212 Type attrType
= attr
.GetType ();
213 if (attributeType
!= null && !attributeType
.IsAssignableFrom (attrType
))
218 if (attributeType
== null || attributeType
.IsValueType
)
219 array
= new Attribute
[a
.Count
];
221 array
= Array
.CreateInstance (attributeType
, a
.Count
) as object[];
227 var attributeInfos
= new Dictionary
<Type
, AttributeInfo
> (initialSize
);
228 int inheritanceLevel
= 0;
229 a
= new List
<object> (initialSize
);
232 foreach (object attr
in res
) {
233 AttributeUsageAttribute usage
;
235 throw new CustomAttributeFormatException ("Invalid custom attribute format");
237 Type attrType
= attr
.GetType ();
238 if (attributeType
!= null) {
239 if (!attributeType
.IsAssignableFrom (attrType
))
243 AttributeInfo firstAttribute
;
244 if (attributeInfos
.TryGetValue (attrType
, out firstAttribute
))
245 usage
= firstAttribute
.Usage
;
247 usage
= RetrieveAttributeUsage (attrType
);
249 // only add attribute to the list of attributes if
250 // - we are on the first inheritance level, or the attribute can be inherited anyway
252 // - multiple attributes of the type are allowed
254 // - this is the first attribute we've discovered
256 // - the attribute is on same inheritance level than the first
257 // attribute that was discovered for this attribute type ))
258 if ((inheritanceLevel
== 0 || usage
.Inherited
) && (usage
.AllowMultiple
||
259 (firstAttribute
== null || (firstAttribute
!= null
260 && firstAttribute
.InheritanceLevel
== inheritanceLevel
))))
263 if (firstAttribute
== null)
264 attributeInfos
.Add (attrType
, new AttributeInfo (usage
, inheritanceLevel
));
267 if ((btype
= GetBase (btype
)) != null) {
269 res
= GetCustomAttributesBase (btype
, attributeType
, true);
271 } while (inherit
&& btype
!= null);
273 if (attributeType
== null || attributeType
.IsValueType
)
274 array
= new Attribute
[a
.Count
];
276 array
= Array
.CreateInstance (attributeType
, a
.Count
) as object[];
278 // copy attributes to array
284 internal static object[] GetCustomAttributes (ICustomAttributeProvider obj
, bool inherit
)
287 throw new ArgumentNullException ("obj");
290 return (object[]) GetCustomAttributesBase (obj
, null, false).Clone ();
292 return GetCustomAttributes (obj
, typeof (MonoCustomAttrs
), inherit
);
295 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
296 [PreserveDependency(".ctor(System.Reflection.ConstructorInfo,System.Reflection.Assembly,System.IntPtr,System.UInt32)", "System.Reflection.CustomAttributeData")]
297 [PreserveDependency(".ctor(System.Reflection.MemberInfo,System.Object)", "System.Reflection.CustomAttributeNamedArgument")]
298 [PreserveDependency(".ctor(System.Type,System.Object)", "System.Reflection.CustomAttributeTypedArgument")]
299 static extern CustomAttributeData
[] GetCustomAttributesDataInternal (ICustomAttributeProvider obj
);
301 internal static IList
<CustomAttributeData
> GetCustomAttributesData (ICustomAttributeProvider obj
, bool inherit
= false)
304 throw new ArgumentNullException (nameof (obj
));
307 return GetCustomAttributesDataBase (obj
, null, false);
309 return GetCustomAttributesData (obj
, typeof (MonoCustomAttrs
), inherit
);
312 internal static IList
<CustomAttributeData
> GetCustomAttributesData (ICustomAttributeProvider obj
, Type attributeType
, bool inherit
)
315 throw new ArgumentNullException (nameof (obj
));
316 if (attributeType
== null)
317 throw new ArgumentNullException (nameof (attributeType
));
319 if (attributeType
== typeof (MonoCustomAttrs
))
320 attributeType
= null;
322 const string Message
= "Invalid custom attribute data format";
323 IList
<CustomAttributeData
> r
;
324 IList
<CustomAttributeData
> res
= GetCustomAttributesDataBase (obj
, attributeType
, false);
326 if (!inherit
&& res
.Count
== 1) {
328 throw new CustomAttributeFormatException (Message
);
329 if (attributeType
!= null) {
330 if (attributeType
.IsAssignableFrom (res
[0].AttributeType
))
331 r
= new CustomAttributeData
[] { res [0] }
;
333 r
= Array
.Empty
<CustomAttributeData
> ();
335 r
= new CustomAttributeData
[] { res [0] }
;
341 if (inherit
&& GetBase (obj
) == null)
344 // if AttributeType is sealed, and Inherited is set to false, then
345 // there's no use in scanning base types
346 if ((attributeType
!= null && attributeType
.IsSealed
) && inherit
) {
347 var usageAttribute
= RetrieveAttributeUsage (attributeType
);
348 if (!usageAttribute
.Inherited
)
352 var initialSize
= Math
.Max (res
.Count
, 16);
353 List
<CustomAttributeData
> a
= null;
354 ICustomAttributeProvider btype
= obj
;
356 /* Non-inherit case */
358 if (attributeType
== null) {
359 foreach (CustomAttributeData attrData
in res
) {
360 if (attrData
== null)
361 throw new CustomAttributeFormatException (Message
);
364 var result
= new CustomAttributeData
[res
.Count
];
365 res
.CopyTo (result
, 0);
368 a
= new List
<CustomAttributeData
> (initialSize
);
369 foreach (CustomAttributeData attrData
in res
) {
370 if (attrData
== null)
371 throw new CustomAttributeFormatException (Message
);
372 if (!attributeType
.IsAssignableFrom (attrData
.AttributeType
))
382 var attributeInfos
= new Dictionary
<Type
, AttributeInfo
> (initialSize
);
383 int inheritanceLevel
= 0;
384 a
= new List
<CustomAttributeData
> (initialSize
);
387 foreach (CustomAttributeData attrData
in res
) {
388 AttributeUsageAttribute usage
;
389 if (attrData
== null)
390 throw new CustomAttributeFormatException (Message
);
392 Type attrType
= attrData
.AttributeType
;
393 if (attributeType
!= null) {
394 if (!attributeType
.IsAssignableFrom (attrType
))
398 AttributeInfo firstAttribute
;
399 if (attributeInfos
.TryGetValue (attrType
, out firstAttribute
))
400 usage
= firstAttribute
.Usage
;
402 usage
= RetrieveAttributeUsage (attrType
);
404 // The same as for CustomAttributes.
406 // Only add attribute to the list of attributes if
407 // - we are on the first inheritance level, or the attribute can be inherited anyway
409 // - multiple attributes of the type are allowed
411 // - this is the first attribute we've discovered
413 // - the attribute is on same inheritance level than the first
414 // attribute that was discovered for this attribute type ))
415 if ((inheritanceLevel
== 0 || usage
.Inherited
) && (usage
.AllowMultiple
||
416 (firstAttribute
== null || (firstAttribute
!= null
417 && firstAttribute
.InheritanceLevel
== inheritanceLevel
))))
420 if (firstAttribute
== null)
421 attributeInfos
.Add (attrType
, new AttributeInfo (usage
, inheritanceLevel
));
424 if ((btype
= GetBase (btype
)) != null) {
426 res
= GetCustomAttributesDataBase (btype
, attributeType
, true);
428 } while (inherit
&& btype
!= null);
433 internal static IList
<CustomAttributeData
> GetCustomAttributesDataBase (ICustomAttributeProvider obj
, Type attributeType
, bool inheritedOnly
)
435 CustomAttributeData
[] attrsData
;
436 if (IsUserCattrProvider (obj
)) {
437 //FIXME resolve this case if it makes sense. Assign empty array for now.
438 //attrsData = obj.GetCustomAttributesData(attributeType, true);
439 attrsData
= Array
.Empty
<CustomAttributeData
> ();
441 attrsData
= GetCustomAttributesDataInternal (obj
);
444 // All pseudo custom attributes are Inherited = false hence we can avoid
445 // building attributes data array which would be discarded by inherited checks
447 if (!inheritedOnly
) {
448 CustomAttributeData
[] pseudoAttrsData
= GetPseudoCustomAttributesData (obj
, attributeType
);
449 if (pseudoAttrsData
!= null) {
450 if (attrsData
.Length
== 0)
451 return Array
.AsReadOnly (pseudoAttrsData
);
452 CustomAttributeData
[] res
= new CustomAttributeData
[attrsData
.Length
+ pseudoAttrsData
.Length
];
453 Array
.Copy (attrsData
, res
, attrsData
.Length
);
454 Array
.Copy (pseudoAttrsData
, 0, res
, attrsData
.Length
, pseudoAttrsData
.Length
);
455 return Array
.AsReadOnly (res
);
459 return Array
.AsReadOnly (attrsData
);
462 internal static CustomAttributeData
[] GetPseudoCustomAttributesData (ICustomAttributeProvider obj
, Type attributeType
)
464 CustomAttributeData
[] pseudoAttrsData
= null;
466 /* FIXME: Add other types */
467 if (obj
is RuntimeMethodInfo monoMethod
)
468 pseudoAttrsData
= monoMethod
.GetPseudoCustomAttributesData ();
469 else if (obj
is RuntimeFieldInfo fieldInfo
)
470 pseudoAttrsData
= fieldInfo
.GetPseudoCustomAttributesData ();
471 else if (obj
is RuntimeParameterInfo monoParamInfo
)
472 pseudoAttrsData
= monoParamInfo
.GetPseudoCustomAttributesData ();
473 else if (obj
is Type t
)
474 pseudoAttrsData
= GetPseudoCustomAttributesData (t
);
476 if ((attributeType
!= null) && (pseudoAttrsData
!= null)) {
477 for (int i
= 0; i
< pseudoAttrsData
.Length
; ++i
) {
478 if (attributeType
.IsAssignableFrom (pseudoAttrsData
[i
].AttributeType
)) {
479 if (pseudoAttrsData
.Length
== 1)
480 return pseudoAttrsData
;
482 return new CustomAttributeData
[] { pseudoAttrsData[i] }
;
486 return Array
.Empty
<CustomAttributeData
> ();
489 return pseudoAttrsData
;
492 static CustomAttributeData
[] GetPseudoCustomAttributesData (Type type
)
495 var Attributes
= type
.Attributes
;
497 /* IsSerializable returns true for delegates/enums as well */
498 if ((Attributes
& TypeAttributes
.Serializable
) != 0)
500 if ((Attributes
& TypeAttributes
.Import
) != 0)
505 CustomAttributeData
[] attrsData
= new CustomAttributeData
[count
];
508 if ((Attributes
& TypeAttributes
.Serializable
) != 0)
509 attrsData
[count
++] = new CustomAttributeData ((typeof (SerializableAttribute
)).GetConstructor (Type
.EmptyTypes
));
510 if ((Attributes
& TypeAttributes
.Import
) != 0)
511 attrsData
[count
++] = new CustomAttributeData ((typeof (ComImportAttribute
)).GetConstructor (Type
.EmptyTypes
));
516 internal static bool IsDefined (ICustomAttributeProvider obj
, Type attributeType
, bool inherit
)
518 if (attributeType
== null)
519 throw new ArgumentNullException ("attributeType");
521 AttributeUsageAttribute usage
= null;
523 if (IsUserCattrProvider (obj
))
524 return obj
.IsDefined (attributeType
, inherit
);
526 if (IsDefinedInternal (obj
, attributeType
))
529 object[] pseudoAttrs
= GetPseudoCustomAttributes (obj
, attributeType
);
530 if (pseudoAttrs
!= null) {
531 for (int i
= 0; i
< pseudoAttrs
.Length
; ++i
)
532 if (attributeType
.IsAssignableFrom (pseudoAttrs
[i
].GetType ()))
540 usage
= RetrieveAttributeUsage (attributeType
);
541 if (!usage
.Inherited
)
546 } while (obj
!= null);
551 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
552 internal static extern bool IsDefinedInternal (ICustomAttributeProvider obj
, Type AttributeType
);
554 static PropertyInfo
GetBasePropertyDefinition (RuntimePropertyInfo property
)
556 MethodInfo method
= property
.GetGetMethod (true);
557 if (method
== null || !method
.IsVirtual
)
558 method
= property
.GetSetMethod (true);
559 if (method
== null || !method
.IsVirtual
)
562 MethodInfo baseMethod
= ((RuntimeMethodInfo
)method
).GetBaseMethod ();
563 if (baseMethod
!= null && baseMethod
!= method
) {
564 ParameterInfo
[] parameters
= property
.GetIndexParameters ();
565 if (parameters
!= null && parameters
.Length
> 0) {
566 Type
[] paramTypes
= new Type
[parameters
.Length
];
567 for (int i
=0; i
< paramTypes
.Length
; i
++)
568 paramTypes
[i
] = parameters
[i
].ParameterType
;
569 return baseMethod
.DeclaringType
.GetProperty (property
.Name
, property
.PropertyType
,
572 return baseMethod
.DeclaringType
.GetProperty (property
.Name
, property
.PropertyType
);
579 static EventInfo
GetBaseEventDefinition (RuntimeEventInfo evt
)
581 MethodInfo method
= evt
.GetAddMethod (true);
582 if (method
== null || !method
.IsVirtual
)
583 method
= evt
.GetRaiseMethod (true);
584 if (method
== null || !method
.IsVirtual
)
585 method
= evt
.GetRemoveMethod (true);
586 if (method
== null || !method
.IsVirtual
)
589 MethodInfo baseMethod
= ((RuntimeMethodInfo
)method
).GetBaseMethod ();
590 if (baseMethod
!= null && baseMethod
!= method
) {
591 BindingFlags flags
= method
.IsPublic
? BindingFlags
.Public
: BindingFlags
.NonPublic
;
592 flags
|= method
.IsStatic
? BindingFlags
.Static
: BindingFlags
.Instance
;
594 return baseMethod
.DeclaringType
.GetEvent (evt
.Name
, flags
);
599 // Handles Type, RuntimePropertyInfo and RuntimeMethodInfo.
600 // The runtime has also cases for RuntimeEventInfo, RuntimeFieldInfo, Assembly and ParameterInfo,
601 // but for those we return null here.
602 static ICustomAttributeProvider
GetBase (ICustomAttributeProvider obj
)
608 return ((Type
) obj
).BaseType
;
610 MethodInfo method
= null;
611 if (obj
is RuntimePropertyInfo
)
612 return GetBasePropertyDefinition ((RuntimePropertyInfo
) obj
);
613 else if (obj
is RuntimeEventInfo
)
614 return GetBaseEventDefinition ((RuntimeEventInfo
)obj
);
615 else if (obj
is RuntimeMethodInfo
)
616 method
= (MethodInfo
) obj
;
617 if (obj
is RuntimeParameterInfo parinfo
) {
618 var member
= parinfo
.Member
;
619 if (member
is MethodInfo
) {
620 method
= (MethodInfo
)member
;
621 MethodInfo bmethod
= ((RuntimeMethodInfo
)method
).GetBaseMethod ();
622 if (bmethod
== method
)
624 return bmethod
.GetParameters ()[parinfo
.Position
];
628 * ParameterInfo -> null
630 * RuntimeEventInfo -> null
631 * RuntimeFieldInfo -> null
633 if (method
== null || !method
.IsVirtual
)
636 MethodInfo baseMethod
= ((RuntimeMethodInfo
)method
).GetBaseMethod ();
637 if (baseMethod
== method
)
643 private static AttributeUsageAttribute
RetrieveAttributeUsageNoCache (Type attributeType
)
645 if (attributeType
== typeof (AttributeUsageAttribute
))
646 /* Avoid endless recursion */
647 return new AttributeUsageAttribute (AttributeTargets
.Class
);
649 AttributeUsageAttribute usageAttribute
= null;
650 object[] attribs
= GetCustomAttributes (attributeType
, typeof(AttributeUsageAttribute
), false);
651 if (attribs
.Length
== 0)
653 // if no AttributeUsage was defined on the attribute level, then
654 // try to retrieve if from its base type
655 if (attributeType
.BaseType
!= null)
657 usageAttribute
= RetrieveAttributeUsage (attributeType
.BaseType
);
660 if (usageAttribute
!= null)
662 // return AttributeUsage of base class
663 return usageAttribute
;
666 // return default AttributeUsageAttribute if no AttributeUsage
667 // was defined on attribute, or its base class
668 return DefaultAttributeUsage
;
670 // check if more than one AttributeUsageAttribute has been specified
672 // NOTE: compilers should prevent this, but that doesn't prevent
673 // anyone from using IL ofcourse
674 if (attribs
.Length
> 1)
676 throw new FormatException ("Duplicate AttributeUsageAttribute cannot be specified on an attribute type.");
679 return ((AttributeUsageAttribute
) attribs
[0]);
682 static AttributeUsageAttribute
RetrieveAttributeUsage (Type attributeType
)
684 AttributeUsageAttribute usageAttribute
= null;
685 /* Usage a thread-local cache to speed this up, since it is called a lot from GetCustomAttributes () */
686 if (usage_cache
== null)
687 usage_cache
= new Dictionary
<Type
, AttributeUsageAttribute
> ();
688 if (usage_cache
.TryGetValue (attributeType
, out usageAttribute
))
689 return usageAttribute
;
690 usageAttribute
= RetrieveAttributeUsageNoCache (attributeType
);
691 usage_cache
[attributeType
] = usageAttribute
;
692 return usageAttribute
;
695 private static readonly AttributeUsageAttribute DefaultAttributeUsage
=
696 new AttributeUsageAttribute (AttributeTargets
.All
);
698 private class AttributeInfo
700 private AttributeUsageAttribute _usage
;
701 private int _inheritanceLevel
;
703 public AttributeInfo (AttributeUsageAttribute usage
, int inheritanceLevel
)
706 _inheritanceLevel
= inheritanceLevel
;
709 public AttributeUsageAttribute Usage
717 public int InheritanceLevel
721 return _inheritanceLevel
;