litbaza книги онлайнРазная литератураCrystal Programming. Введение на основе проекта в создание эффективных, безопасных и читаемых веб-приложений и приложений CLI - Джордж Дитрих

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 53 54 55 56 57 58 59 60 61 ... 75
Перейти на страницу:
перебор методов типа.

Итерационные методы

Итерирующие методы имеют много общего с итерирующими типами, только с другим типом макроса. Первое, что нам нужно для перебора методов, — это TypeNode, представляющий тип, методы которого нас интересуют. Отсюда мы можем вызвать метод #methods, который возвращает ArrayLiteral(Def) всех методов, определенных для этого типа. Например, давайте напечатаем массив всех имен методов внутри класса:

abstract class Foo

   def foo; end

end

module Bar

   def bar; end

end

class Baz < Foo

   include Bar

   def baz; end

   def foo(value : Int32); end

   def foo(value : String); end

   def bar(x); end

end

baz = Baz.new

baz.bar 1

baz.bar false

{{pp Baz.methods.map &.name}}

Запуск этого приведет к следующему:

[baz, foo, foo, bar]

Обратите внимание, что, как и в случае с методом #includers, выводятся только методы, явно определенные внутри типа. Также обратите внимание, что метод #foo включается один раз для каждой из его перегрузок. Однако, несмотря на то, что #bar вызывается с двумя уникальными типами, он включается только один раз.

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

Если вы помните раздел об итерации переменных экземпляра ранее в этой главе, для доступа к переменным класса существовал специальный метод TypeNode#class_vars. В случае методов класса эквивалентного метода не существует. Однако их можно перебирать. В большинстве случаев TypeNode будет представлять тип экземпляра типа, поэтому он используется для перебора переменных экземпляра или методов экземпляра этого типа. Однако существует метод, который можно использовать для получения другого TypeNode, представляющего метакласс этого типа, из которого мы можем получить доступ к методам его класса. Существует также метод, который возвращает тип экземпляра, если TypeNode представляет тип класса.

Этими методами являются TypeNode#class и TypeNode#instance. Например, если у вас есть TypeNode, представляющий тип MyClass, первый метод вернет новый TypeNode, представляющий MyClass.class, тогда как последний метод превратит MyClass.class в MyClass. Когда у нас есть тип класса TypeNode, это так же просто, как вызвать для него #methods; например:

class Foo

   def self.foo; end

   def self.bar; end

end

{{pp Foo.class.methods.map &.name}}

Запуск этого приведет к следующему:

[allocate, foo, bar]

Вам может быть интересно, откуда взялся метод allocate. Этот метод автоматически добавляется Crystal для использования в конструкторе, чтобы выделить память, необходимую для его создания. Учитывая, что вы, скорее всего, не захотите включать этот метод в свою логику, обязательно предусмотрите способ его отфильтровать.

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

Резюме

И вот оно у вас есть; как анализировать переменные, типы и методы экземпляра/класса во время компиляции! Этот метод метапрограммирования можно использовать для создания мощной логики генерации кода, которая может упростить расширение и использование приложений, одновременно делая приложение более надежным за счет снижения вероятности опечаток или ошибок пользователя.

Далее, в последней главе этой части, мы рассмотрим несколько примеров того, как все изученные до сих пор концепции метапрограммирования можно объединить в более сложные шаблоны/функции.

Дальнейшее чтение

Как упоминалось ранее, в TypeNode есть гораздо больше методов, которые находятся за пределами области видимости. Однако я настоятельно рекомендую ознакомиться с документацией по адресу https://crystal-lang.org/api/Crystal/Macros/TypeNode.html, чтобы узнать больше о том, какие дополнительные данные могут быть извлечены.

13. Расширенное использование макросов

В последних нескольких главах мы рассмотрели различные концепции метапрограммирования, такие как макросы, аннотации, и то, как их можно использовать вместе, чтобы обеспечить самоанализ типов, методов и переменных экземпляра во время компиляции. Однако по большей части мы использовали их самостоятельно. Эти концепции также можно комбинировать, чтобы создавать еще более мощные шаборны! В этой главе мы собираемся изучить некоторые из них, в том числе:

• Использование аннотаций для влияния на логику времени выполнения.

• Представление данных аннотаций/типов во время выполнения.

• Определение значения константы во время компиляции.

• Создание собственных ошибок времени компиляции.

К концу этой главы вы должны иметь более глубокое понимание метапрограммирования в Crystal. У вас также должны быть некоторые идеи о неочевидных вариантах использования метапрограммирования, которые позволят вам создавать уникальные решения проблем в вашем приложении.

Технические требования

Прежде чем мы углубимся в эту главу, в вашей системе должно быть установлено следующее:

• Рабочая установка Crystal.

Инструкции по настройке Crystal можно найти в Главе 1 «Введение в Crystal».

Все примеры кода, использованные в этой главе, можно найти в папке Chapter13 на GitHub: https://github.com/PacktPublishing/Crystal-Programming/tree/main/Chapter13.

Использование аннотаций для влияния на логику времени выполнения

Как мы узнали в Главе 11 «Введение в аннотации», аннотации — это отличный способ добавить дополнительные метаданные к различным функциям Crystal, таким как типы, переменные экземпляра и методы. Однако одним из их основных ограничений является то, что хранящиеся в них данные доступны только во время компиляции.

В некоторых случаях вам может потребоваться реализовать функцию с использованием аннотаций для настройки чего-либо, но логика, требующая этих данных, не может быть сгенерирована только с помощью макросов и должна выполняться во время выполнения. Например, предположим, что мы хотим иметь возможность печатать экземпляры объектов в различных форматах. Эта логика может использовать аннотации, чтобы отметить, какие переменные экземпляра следует предоставлять, а также настроить способ их форматирования. Высокоуровневый пример этого будет выглядеть так:

annotation Print; end

class MyClass

   include Printable

   @[Print]

   property name : String = "Jim"

   @[Print(format: "%F")]

   property

1 ... 53 54 55 56 57 58 59 60 61 ... 75
Перейти на страницу:

Комментарии
Минимальная длина комментария - 20 знаков. Уважайте себя и других!
Комментариев еще нет. Хотите быть первым?