Say I have this class:
[AttributeUsage(AttributeTargets.Method)]
public class MyAttribute : Attribute
{
public MyAttribute()
{
// Do stuff
}
~MyAttribute()
{
// When is this called? When the function ends? Whenever the GC feels like?
}
}
Following an example call of GetCustomAttributes through in Reflector, the managed part of the code (i.e. the point at which it transitions to the runtime and becomes an external call) is down in CustomAttribute.GetCustomAttributes.
At this point, the method is examining the bytes of the metadata surrounding the object for which the attributes are being loaded.
There is code in there which then does further reflection to find the runtime constructor being called. E.g.
Would be calling the default, whereas
Would be calling a constructor that takes
(Int, String, Type).I can’t see any evidence in there that any caching of instances is performed which therefore means that attribute instances are created on demand when they are reflected.
Proof
The aforementioned managed-runtime transition happends at CustomAttribute._CreateCaObject. Whilst not easy to statically analyse whether this method does in fact cache the instances it creates (it does potentially get enough state information in the form of a memory buffer pointer presumably indicating the location in metadata where the attribute declaration resides) we can look at the facts:
This tells me that the attribute is always constructed.
We can test for this, of course, by writing a piece of code in a test.
Therefore an efficient use of metadata attributes would be to cache them in user code, unless of course the attribute is mutable in some way that makes it not applicable for all instances of a given
T, or all ‘instances’ (in quotes because of course a method is only stored in memory once) of a methodmfor instances of typeT).Following this, therefore, an attribute is available to the GC once all references to it in code have been nulled. The same is true for all members of that attribute as well.
Therefore a method which uses GetCustomAttributes() to retrieve an attribute, uses it and then throws away the reference has just released a new instance of that attribute for the GC to clean up when it needs to.
Therefore – attribute instances are governed by the exact same memory management and lifetime rules as all class instances; therefore what @PieterG says in his answer is correct – the destructor could be called at any time after all references to that attribute have been released.