Получение информации об Custom Attribute-ах из макросов
От: VladD2 Российская Империя www.nemerle.org
Дата: 04.04.10 17:23
Оценка:
#Имя: FAQ.nemerle.GetCustomAttributeInfo001
Здравствуйте, evilbeaver, Вы писали:

E>Каким образом (и возможно ли вообще) получить эти атрибуты в виде собственно объектов атрибутов?


Никаким. До окончания компиляции атрибуты не боле чем выражения (PExpr). Но их можно проанализировать.

E>Пока все мои изычскания в этой области закончились тем, что можно узнать наличие атрибута через Modifiers.IsCustomAttributeDefined.

E>Но дело в том, что атрибут содержит строку и мне нужно ее выцепить.

Это не очень просто, но реализуемо. Как верно заметил hardcase, атрибуты можно вынуть через GetModifiers().GetCustomAttributes(). Проблема в том, что при этом возвращается list[PExpr], то есть список выражений которые описывают атрибуты.

Нужно перебрать эти выражения и найти то что описывает искомый атрибут. Потом нужно разобрать это выражение (с помощью паттерн-матчинга) и извлечь строку.

Ниже приведен пример демонстрирующий этот подход.
Вот макрос применимый к полям позволяющий разобрать кастом-атрибут типа "Ns1.MyAttr":
using Nemerle;
using Nemerle.Collections;
using Nemerle.Compiler;
using Nemerle.Text;
using Nemerle.Utility;

using System;
using System.Collections.Generic;
using System.Linq;

namespace GetInfoFromCustomAttributeMacro
{
  [Nemerle.MacroUsage (MacroPhase.WithTypedMembers, MacroTargets.Field, Inherited = true, AllowMultiple = false)]
  public macro TestMacro(tb : TypeBuilder, fld : FieldBuilder)
  {
    Helper.DoWork(Nemerle.Macros.ImplicitCTX(), tb, fld)
  }
  
  module Helper
  {
    public DoWork(typer : Typer, _tb : TypeBuilder, fld : FieldBuilder) : void
    {
      def attrs = fld.GetModifiers().GetCustomAttributes();
      //assert2(false); // если раскоментарить, то можно будет поглядеть на работу макроса под отладчиком!
      foreach (a in attrs)
      {
        | <[ $attr($(str : string)) ]> => // Распознаем атрибут которому передан один не именованный параметр типа string.
          def typedAttr = typer.BindFixedType(attr); // пытаемся связать "имя макроса" с имеющимися на время компиляции типами.
          
          // Теперь мы можем сверять полное имя типа. Если связыание не удастся, будет выдано сообщение об ошибке.
          when (typedAttr.TypeInfo.FullName == "Ns1.MyAttr")
            Message.Hint(a.Location, $"Attribute: '$a' attr: '$attr' str: '$str' typedAttr: '$typedAttr'");
          
        | _ => ()
      }
    }
  }
}

А вот применение данного макроса:
using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;

using System;
using System.Collections.Generic;
using System.Console;
using System.Linq;
using GetInfoFromCustomAttributeMacro;

using Ns1;

namespace Ns1
{
  [Record]
  public class MyAttr : Attribute
  {
    public AttrField : string;
  }
}

namespace Ns2
{
  [Record]
  public class MyAttr : Attribute
  {
    public AttrField : string;
  }
}

module Program
{
  [Ns1.MyAttr("Test str"), TestMacro, MyAttr("Other string"), Ns2.MyAttr("Test str")]
  field1 : int = 0;
  
  Main() : void
  {
    _ = field1;
    WriteLine("Hi!");
    _ = ReadLine();
  }
}


При компиляции проекта (надо не забыть подключить ссылка на маро-проект в тестовый проект) будет выданы два предупреждения:

Main.n(33,4):Warning: hint: Attribute: 'Ns1.MyAttr("Test str")' attr: 'Ns1.MyAttr' str: 'Test str' typedAttr: 'Ns1.MyAttr'
Main.n(33,39):Warning: hint: Attribute: 'MyAttr("Other string")' attr: 'MyAttr' str: 'Other string' typedAttr: 'Ns1.MyAttr'


Обратите внимание, что на атрибут "Ns1.MyAttr" сообщение не выдано!

ЗЫ

Это что касается получения значений атрибутов которые уже объявлены в
библиотеках или объявляются в конечном проекте.

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