Это мой перевод анонса так называемых type helpers,
сделанного в феврале 2013 года.
На мой взгляд, он достаточно хорошо и подробно описывает эту фичу.
Оригинальный текст письма доступен здесь.
Приветствую сообщество Free Pascal!
Я рад представить дополнение к хелперам, которое распространяет
существующую концепцию хелперов на примитивные
типы.
Мотивация
С хелперами для классов и записей появилась возможность расширять
классы и записи без их наследования (что всё равно было бы невозможно для
записей). К примеру, это позволяет добавлять методы типам из других модулей,
на которые вы не можете влиять напрямую, или не можете повлиять на экземпляр
(например, используемый в TMemo наследник TStrings). Сейчас логично
расширить это также и на другие типы, поддерживаемые Паскалём, но теперь
нами движет в большей степени возможность сгруппировать методы вместе для
примитивных типов.
Здесь не будет идти речь об использовании в паскале концепции объекта-обёртки в
управляемых языках/окружениях, таких как Java и .NET, но всё это можно
рассматривать и с этой точки зрения.
Синтаксис
Объявление хелперов для типов выглядит следующим образом:
TYPENAME = type helper[(BASEHELPER)] for EXTENDEDTYPE
DECLARATIONS
end;
Как и в случае классов и записей, они поддерживают все видимые секции, и
вы можете определять методы, свойства и конструкторы. Внутри методов,
объявленных в хелпере, «Self» будет иметь расширенный тип и его значение
также может быть изменено.
Аналогично хелперам для записей, классовые методы ДОЛЖНЫ быть объявлены
статичными. (Прим. пер.: т.е. использована директива static.)
Использование
Хелперы для типов активны, если они видимы. Это означает, что либо они должны
быть объявлены в том же модуле до использующего их кода, либо они должны быть
объявлены в используемом модуле. Как и в случае с хелперами для классов и
записей, только один типовой хелпер может быть активен для одного заданного
типа и вы должны следить за видимостью, когда используете хелперы (к примеру,
в текущем модуле сперва проверяется implementation секция, затем interface
(если код в implementation секции) и затем используемые модули просматриваются
справа налево и каждый модуль сверху вниз).
В некоторых случаях смысл типа зависит от настроек компилятора, с которыми
хелпер был скомпилирован. К примеру, тип Integer может означать либо ShortInt,
либо LongInt в зависимости от текущего режима (fpc/tp и objfpc/delphi
соответственно) и тип String отличается в зависимости от переключателей
{$H+/-} и {$modeswitch unicodestring}. Это нужно помнить при использовании
их в «generic» типах. Другой специфичный случай — тип Extended на платформах,
которые не поддерживают этот тип (и поэтому могут быть определены как Double).
Кроме того, тип, объявленный как «NewType = type OldType», считается полностью
независимым типом, к примеру в случае перегрузки операторов.
Если хелпер для типа находится в области видимости, вы можете без проблем
вызывать его методы и свойства как будто вы работаете с классами или записями.
Давайте предположим, что у нас есть хелпер с методом «ToString: String» для
типа LongInt в области видимости, тогда выглядеть это будет как-то так:
var
i: LongInt;
begin
Writeln(i.ToString);
end.
Кроме вызова типовых хелперов на переменных, их можно также
вызвать на константах, но нужно позаботиться о том, что будет использован
корректный тип. К примеру, константа «200» будет иметь типа «Byte», тогда
как «20» — тип «SmallInt». Кроме того, тип строковых констант зависит от
текущего используемого режима (в особеноости от {$H+/-} и
{$modeswitch unicodestring}) и содержимого строки. К примеру, в случае
"{$mode objfpc}{$H+}" строка, содержащая юникодные символы, будет
рассматриваться как константа типа UnicodeString и поэтому только хелперы
для UnicodeString будут применены.
Для следующего примера предположим, что хелпер из предыдущего примера
опять находится в области видимости:
begin
Writeln($12345678.ToString);
end.
Также допустимы указатели (к примеру, "@i") (тип: Pointer) и встроенные
константы: "True" (тип: Boolean), "False" (тип: Boolean) и "Nil" (тип: Pointer).
Пожалуйста, обратите внимание, что в случае типизированных указателей будет
использован нетипизированный указатель, потому что соответствующее имя типа
не может быть доступно в текущем модуле (а значит, хелпер не может быть
найден). Константы, которые не могут быть использованы с типовыми хелперами —
множества и конструкторы массивов.
Классовые методы (и свойства) могут быть применены не только к переменным
и константам, но и к самим типам:
type
TLongIntHelper = type helper for LongInt
class procedure Test; static;
end;
class procedure TLongIntHelper.Test;
begin
Writeln('Test');
end;
var
i: LongInt;
begin
i.Test;
$12345678.Test;
LongInt.Test;
end.
Поддерживаемые типы
Имеются ограничения на типы, которые могут быть расширены. Любой примитивный
тип может быть расширен, за исключением:
- файловых типов (TextFile, file of ...)
- процедурных переменных
Конечно, также запрещены записи, классы, Objective C классы, C++ классы, объекты и интерфейсы.
Если смотреть с другой стороны, то это значит, что следующие типы разрешены:
- порядковые типы
- сроковые типы (включая символьные типы)
- множества и перечислимые типы
- массивы
- булевские типы
- Variant
- типы чисел с плавающей точкой
- указатели
Совместимость с Delphi
Эта возможность введена в Delphi XE3, но вместо использования более логичного
синтаксиса «type helper» была добавлена поддержка для примитивных типов
«record helper». Поэтому для поддержания совместимости с Delphi такой синтаксис
также используется в Delphi режиме. Кроме того, в не Delphi режимах поддерживается наследование
для типовых хелперов.
Будущее развитие
- Добавить поддержку для большего числа типов, особенно для интерфейсов и
объектов. Они могут быть добавлены либо как классовые хелперы, либо как
«object helper» и «interface helper» (т.к. объекты и интерфейсы поддерживают
наследование правил видимостей, внутри они напоминают хелперы для классов
больше, чем хелперы для записей/типов).
- Позволить нескольким хелперам быть активными для одного типа в одно и то же
время. Это одно из основных ограничений текущей реализации хелперов. Для
этого нужно продумать соответствующие правила поиска, а возможность
добавить как modeswitch (быть может, по умолчанию включив её в objfpc).
- Добавить поддержку хелперов в JVM версии. Наиболее разумным подходом в
реализации этого кажется передавать переменную «Self» как дополнительный
параметр и рассматривать методы как статичные (как это сделано в .NET
для методов в хелперах).
С уважением,
Свен
Читать дальше......