После примера вызова GetCustomAttributes в Reflector управляемая часть кода (т. Е. Точка, в которой он переходит в среду выполнения и становится внешним вызовом) недоступна в 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
или для всех ' экземпляры '(в кавычках, потому что метод хранится в памяти только один раз) метода m
для экземпляров типа T
).
Следовательно, после этого атрибут становится доступным для GC после того, как все ссылки на него в коде были обнулены. То же самое верно и для всех членов этого атрибута.
Поэтому метод, который использует GetCustomAttributes () для извлечения атрибута, использует его и затем выбрасывает ссылку, только что выпустил новый экземпляр этого атрибута для GC, чтобы очистить, когда это необходимо.
Следовательно - экземпляры атрибутов регулируются точно такими же правилами управления памятью и правилами жизни, как и все экземпляры классов; поэтому то, что @PieterG говорит в своем ответе, является правильным - деструктор может быть вызван в любое время после того, как все ссылки на этот атрибут были освобождены.