说我有这个班:
[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?
}
}
最佳答案 在Reflector中调用GetCustomAttributes之后,代码的托管部分(即它转换到运行时并成为外部调用的点)在CustomAttribute.GetCustomAttributes中关闭.
此时,该方法正在检查正在加载属性的对象周围的元数据的字节.
那里有代码,然后进行进一步的反射以找到被调用的运行时构造函数.例如.
[MyAttribute]
将调用默认值,而
[MyAttribute(1, "hello", typeof(T))]
将调用一个构造函数(Int,String,Type).
我看不到任何证据表明执行了任何实例缓存,因此意味着属性实例在反映时按需创建.
证明
上述托管运行时转换发生在CustomAttribute._CreateCaObject.虽然不容易静态分析这个方法是否确实缓存了它创建的实例(它确实可能以内存缓冲区指针的形式获得足够的状态信息,可能指示属性声明所在的元数据中的位置),我们可以看一下事实:
>始终调用构造函数,并且
>始终读取并输入新的构造函数参数.
这告诉我该属性总是被构造.
当然,我们可以通过在测试中编写一段代码来测试它.
[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");
}
}
}
因此,元数据属性的有效使用将是将它们缓存在用户代码中,除非该属性在某种程度上是可变的,使得它不适用于给定T的所有实例或所有“实例”(因为当然是引号)对于类型T的实例,方法仅存储在方法m的存储器中.
因此,在此之后,只要代码中对它的所有引用都已为空,就可以使用该属性.对于该属性的所有成员也是如此.
因此,使用GetCustomAttributes()检索属性,使用它然后抛弃引用的方法刚刚释放了该属性的新实例,以便GC在需要时进行清理.
因此 – 属性实例由与所有类实例完全相同的内存管理和生存期规则控制;因此@PieterG在他的回答中说的是正确的 – 在释放了对该属性的所有引用之后,可以随时调用析构函数.