После примера вызова GetCustomAttributes через рефлектор, управляемую часть коды (т.е. точки, в которой он переходит к выполнению и становится внешним вызовом) вниз в CustomAttribute.GetCustomAttributes.
На этом этапе метод изучает байты метаданных, окружающих объект, для которого загружаются атрибуты.
Существует код, который затем делает дальнейшее отражение, чтобы найти вызываемый конструктор выполнения. Например.
[MyAttribute]
бы называть по умолчанию, в то время как
[MyAttribute(1, "hello", typeof(T))]
бы называть конструктор, который принимает (Int, String, Type)
.
Я не вижу никаких доказательств того, что выполняется какое-либо кеширование экземпляров, что означает, что экземпляры атрибутов создаются по требованию, когда они отражаются.
Доказательство
Вышеупомянутый переход управляемых во время выполнения happends в 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 говорит в своем ответе, правильно - деструктор можно было вызвать в любое время после того, как все ссылки на этот атрибут были выпущены.
Теперь, если мне нужно, чтобы класс атрибута был инстанцирован при вызове функции? –
@the_drow - это можно сделать (посмотрите MethodBase.GetCurrentMethod), но вы не можете сделать это вручную в начале метода. Вы также можете взглянуть на запись суррогата выполнения, который использует выражения (поэтому, которые затем могут отражать метод, который будет вызываться, вытащить атрибут, а затем сделать все, что ему нужно), который затем может выполняться против экземпляра атрибута до выполнения этого метода что вы хотите призвать. Это отдельный вопрос SO, хотя :) –
Есть ли какой-нибудь пример того, как написать такой суррогат выполнения? –