Два других метода, #args
и #named_args
, не возвращают конкретное значение, а вместо этого возвращают коллекцию всех позиционных или именованных аргументов в аннотации в виде TupleLiteral
и NamedTupleLiteral
соответственно.
Прежде всего, давайте посмотрим, как мы можем работать с данными, хранящимися в классе, используя данные из аннотации для создания вывода:
annotation MyClass; end
Annotation MyAnnotation; end
@[MyClass(true, id: "foo_class")]
class Foo
{% begin %}
{% ann = @type.annotation MyClass %}
{% pp "#{@type} has positional arguments of:
#{ann.args}" %}
{% pp "and named arguments of #{ann.named_args}" %}
{% pp %(and is #{ann[0] ? "active".id :
"not active".id}) %}
{% status = if my_ann = @type.annotation MyAnnotation
"DOES"
else
"DOES NOT"
end %}
{% pp "#{@type} #{status.id} have MyAnnotation applied." %}
{% end %}
end
Запуск этой программы выведет следующее:
"Foo has positional arguments of: {true}"
"and named arguments of {id: \"foo_class\"}"
"and is active."
"Foo DOES NOT have MyAnnotation applied."
Мы также можем сделать то же самое с аннотацией, примененной к методу:
annotation MyMethod; end
@[MyMethod(4, 1, 2, id: "foo")]
def my_method
{% begin %}
{% ann = @def.annotation MyMethod %}
{% puts "\n" %}
{% pp "Method #{@def.name} has an id of #{ann[:id]}" %}
{% pp "and has #{ann.args.size} positional arguments" %}
{% total = ann.args.reduce(0) { |acc, v| acc + v } %}
{% pp "that sum to #{total}" %}
{% end %}
end
my_method
Запуск этой программы выведет следующее:
"Method my_method has an id of \"foo\""
"and has 3 positional arguments"
"that sum to 7"
В обоих этих примерах мы использовали все три метода, а также некоторые сами типы коллекций. Мы также увидели, как обрабатывать необязательную аннотацию, следуя той же логике обработки nil
, что и в коде Crystal, не являющемся макросом. Если бы к нашему классу была применена аннотация, мы могли бы получить доступ к любым дополнительным данным из него через переменную my_ann
, так же, как мы это делали с переменной ann
в предыдущих строках. Этот шаблон может быть невероятно полезен, позволяя влиять на логику макроса наличием или отсутствием аннотации. Это может привести к более читабельному коду, для которого в противном случае потребовалась бы одна аннотация со множеством различных полей.
Как и в предыдущем примере с несколькими аннотациями для одного элемента, метод #annotation
возвращает #annotations
. Этот метод работает почти идентично другому методу, но возвращает ArrayLiteral(Annotation)
вместо Annotation?
. Например, мы могли бы использовать этот метод для перебора нескольких аннотаций, чтобы напечатать индекс аннотации вместе со значением, которое она хранит:
annotation MyAnnotation; end
@[MyAnnotation("foo")]
@[MyAnnotation(123)]
@[MyAnnotation(123)]
def annotation_read
{% for ann, idx in @def.annotations(MyAnnotation) %}
{% pp "Annotation #{idx} = #{ann[0].id}" %}
{% end %}
end
annotation_read
Запуск этого приведет к печати следующего:
"Annotation 0 = foo"
"Annotation 1 = 123"
"Annotation 2 = 123"
Вот и все. Аннотации сами по себе являются довольно простой функцией, но могут быть весьма мощными в сочетании с некоторыми другими функциями метапрограммирования Crystal.
Резюме