views:

64

answers:

2

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?
  }
}
+1  A: 

Whenever the GC Feels like it

Destructors cannot be called. They are invoked automatically.

Destructors (C# Programming Guide)

PieterG
and what if I use dispose?
the_drow
@the_drow: dispose has no effect - that's just a mechanism introduced by an interface and has nothing to do with .Net's own memory management
Andras Zoltan
True for destructors (finalizers) but this does not address the special case of attributes
Henk Holterman
It is interesting that an Attribute would have a special case, but as far as I can tell there is no reference in the Destructors documentation that excludes Attributes.
PieterG
@PieterG: Yes but what I am asking is a very rare case, I'm not sure if they thought about it themselves.
the_drow
@Henk and @Pieter: I've always assumed Attributes were special cases; but I've just done some investigation with reflector and, apart from how the constructor parameters are obtained, I was disappointed to find that they appear to be quite boringly handled in much the same way as any type. See if you agree with my answer (I might have missed something in my analysis).
Andras Zoltan
+1  A: 

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.

[MyAttribute]

Would be calling the default, whereas

[MyAttribute(1, "hello", typeof(T))]

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:

  • The constructor is always called, and
  • New constructor parameters are always read and fed in.

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.

[TestMethod]
public void TestMethod1()
{
  //if running in MSTest you have to allow for the test runner to reflect 
  //over the class as it looks for the TestClass attribute - therefore if our
  //assumption is correct that a new instance is always constructed when 
  //reflecting, our counter check should start at 2, not 1.
  Type t = typeof(AttributeTest);
  var attributes = 
    t.GetCustomAttributes(typeof(AttributeTest.TheAttributeAttribute), false);  
  //check counter
  Assert.AreEqual(2, AttributeTest.TheAttributeAttribute.Counter);
  var attributes2 = 
    t.GetCustomAttributes(typeof(AttributeTest.TheAttributeAttribute), false);
  //should be one louder (sorry, 'one bigger' - the Spinal Tap influence :) )
  Assert.AreEqual(3, AttributeTest.TheAttributeAttribute.Counter);
}

[TheAttribute]
public class AttributeTest
{
  public class TheAttributeAttribute : Attribute
  {
    static int _counter = 0;

    public static int Counter { get { return _counter; } }

    public TheAttributeAttribute()
    {
      _counter++;
      Console.WriteLine("New");
    }
  }
}

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 method m for instances of type T).

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.

Andras Zoltan
now what if I need that an attribute class would be instanceted when the function is called?
the_drow
@the_drow - this can be done (look at MethodBase.GetCurrentMethod) but you neeed to do that manually at the start of the method. You can also look at writing an execution surrogate that uses expressions (therefore which can then reflect the method to be called, pull out the attribute and then do whatever it needs to do) which can then execute against the attribute instance before then executing the method that you want to be called.This is a separate SO question, though :)
Andras Zoltan
@Andras Zoltan: Got any example for how to write such an execution surrogate?
the_drow
@the_drow: there are lots of examples of systems out there that do very similar kinds of things - the concept you're talking about is called cross-cutting. Asp.Net MVC uses the same thing for it's Action Filters. I would post another q on SO giving an example of what you want to do - reckon you'll have an answer within the hour :) If I see it I'll come up with something; however I'm also on the clock at the moment :)
Andras Zoltan
@Andras Zoltan: Thank you very much.
the_drow