Генерация и сериализация метаданных во время компиляции с использованием PostSharp

Я пытаюсь сделать что-то, что, по моему мнению, должно быть прямолинейным и простым, но, хоть убей меня, я не могу заставить это работать. Используя PostSharp, я хочу создать аспект, который я применяю к определению класса (или интерфейса), и этот аспект во время компиляции отражает тип, к которому он был применен, а затем сохраняет эту информацию об отражении в переменной, готовой для извлечения в момент время работы.

Я знаю, что функцию CompileTimeInitialize следует использовать для генерации информации об отражении и сохранения ее в переменной. Затем все это сериализуется. Однако я не могу получить информацию, хранящуюся во время выполнения. RuntimeInitialize может видеть переменную и данные в ней, но когда я затем получаю атрибут из типа в другом месте моего кода, используя GetCustomAttributes, атрибут оказывается пустым.

Вот часть кода, который у меня есть до сих пор:

[Serializable]
[MulticastAttributeUsage(MulticastTargets.Class, AllowMultiple = false, PersistMetaData = true)]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class MetadataAttribute : TypeLevelAspect {

    public MetadataAttribute() { }

    private int test;

    public override bool CompileTimeValidate(Type type) {
        this.test = 11;
        return true;
    }

    public override void CompileTimeInitialize(Type type, AspectInfo aspectInfo) {
        this.test = 9;
    }

    public override void RuntimeInitialize(Type type) {
        // When I break here, I can see during debugging, that test is 11 (I would have expected 9 to be honest, but I suspect that CompileTimeInitialize is not executed for a TypeLevelAspect - unless I am mistaken?)            
    }

}

Вот консольное приложение, которое я пытаюсь использовать для извлечения числа:

class Program {
    static void Main(string[] args) {
        var ma = typeof(Test).GetCustomAttribute<MetadataAttribute>();
        var test = new Test();
        var ma2 = test.GetType().GetCustomAttribute<MetadataAttribute>();
        // When I break here, both ma and ma2 have test set to 0.
    }
}

[Metadata]
public class Test { }

Заранее спасибо за любую помощь, это очень ценно и имеет большое значение для того, чтобы мои волосы дольше держались на голове, чем в моих руках :)


person Anupheaus    schedule 29.01.2013    source источник


Ответы (1)


Когда вы используете Type.GetCustomAttributes, вы требуете от CLR создания нового экземпляра настраиваемых атрибутов из их определения, хранящегося в метаданных, т. е. путем вызова конструктора и установки полей и свойств. Таким образом, вы получаете не экземпляр, созданный и инициализированный PostSharp, а полностью новый экземпляр.

Если вы хотите получить доступ к экземпляру PostSharp, вы должны использовать RuntimeInitialize для сохранения этого экземпляра в каком-то общем репозитории экземпляров аспектов, а затем получить к ним доступ из кода. Обратите внимание, что PostSharp работает RuntimeInitialize лениво, поэтому вы не сможете получить доступ к своему экземпляру, пока он не будет инициализирован.

person Gael Fraiteur    schedule 30.01.2013
comment
Спасибо, Гаэль, так что же побуждает вызывать RuntimeInitialize? Есть ли способ заставить его вызываться, прежде чем я проверю этот общий репозиторий? - person Anupheaus; 30.01.2013
comment
Кроме того, почему CompileTimeInitialize, кажется, не выполняется на TypeLevelAspect - или почему я вижу 11 вместо 9 в тесте? - person Anupheaus; 30.01.2013
comment
RuntimeInitialize вызывается перед выполнением аспекта с помощью статического конструктора, нет возможности контролировать, когда он вызывается. CompileTimeValidate запускается после CompileTimeInitialize и переопределяет значение вашего поля. - person Gael Fraiteur; 30.01.2013