В Delphi тип TFieldType представляет собой перечисление (enum), которое определяет стандартные типы полей для работы с базами данных. Однако разработчики часто сталкиваются с необходимостью расширить этот набор собственными типами данных. В этой статье мы рассмотрим, почему прямое расширение TFieldType невозможно и какие альтернативные подходы существуют для решения этой задачи.
Проблема расширения перечислений
Как правильно отметили в комментариях к исходному вопросу, перечисления (enums) и множества (sets) в Object Pascal не могут быть расширены после их объявления. Это ограничение языка:
type
TFieldType = (ftUnknown, ftString, ftSmallint, ftInteger, ftWord, ...);
Попытка "добавить" новые значения к существующему перечислению, как в примере из вопроса:
не будет работать, так как в Object Pascal нет синтаксиса для объединения перечислений.
Альтернативные решения
1. Использование классов-оберток
Наиболее надежный способ - создать собственный класс, который будет содержать как стандартные типы TFieldType, так и ваши пользовательские типы:
type
TCustomFieldType = class
private
FNativeType: TFieldType;
FExtendedType: Integer;
FIsExtended: Boolean;
public
constructor CreateNative(AType: TFieldType);
constructor CreateExtended(AType: Integer);
property IsExtended: Boolean read FIsExtended;
property NativeType: TFieldType read FNativeType;
property ExtendedType: Integer read FExtendedType;
function ToString: string; override;
end;
Реализация методов:
constructor TCustomFieldType.CreateNative(AType: TFieldType);
begin
FNativeType := AType;
FIsExtended := False;
end;
constructor TCustomFieldType.CreateExtended(AType: Integer);
begin
FExtendedType := AType;
FIsExtended := True;
end;
function TCustomFieldType.ToString: string;
begin
if IsExtended then
case FExtendedType of
0: Result := 'ftAlpha';
1: Result := 'ftAlphaNumeric';
2: Result := 'ftNumeric';
else
Result := 'ftCustom' + IntToStr(FExtendedType);
end
else
Result := GetEnumName(TypeInfo(TFieldType), Ord(FNativeType));
end;
2. Использование констант с offset
Другой подход - определить пользовательские типы как константы с большими значениями, которые не пересекаются со стандартными TFieldType:
Затем вы можете использовать эти константы в своем коде, проверяя, является ли тип стандартным или пользовательским:
function FieldTypeToString(FieldType: Integer): string;
begin
if FieldType < 100 then
Result := GetEnumName(TypeInfo(TFieldType), FieldType)
else
case FieldType of
ftAlpha: Result := 'ftAlpha';
ftAlphaNumeric: Result := 'ftAlphaNumeric';
ftNumeric: Result := 'ftNumeric';
else
Result := 'ftCustom' + IntToStr(FieldType);
end;
end;
3. Наследование от существующих классов полей
Если вам нужно добавить функциональность для работы с новыми типами данных, можно создать потомков от существующих классов полей:
type
TAlphaField = class(TStringField)
protected
procedure SetAsString(const Value: string); override;
function GetAsString: string; override;
end;
procedure TAlphaField.SetAsString(const Value: string);
begin
if not ContainsOnlyLetters(Value) then
raise EDatabaseError.Create('Field accepts only alphabetic characters');
inherited;
end;
function TAlphaField.GetAsString: string;
begin
Result := inherited GetAsString;
end;
Затем зарегистрировать этот класс поля:
RegisterFieldClass(TAlphaField);
Работа с пользовательскими типами в SQL-запросах
Если ваши пользовательские типы должны взаимодействовать с базой данных, вам потребуется преобразовывать их в стандартные SQL-типы:
function CustomTypeToSQLType(FieldType: Integer): string;
begin
case FieldType of
ftAlpha, ftAlphaNumeric: Result := 'VARCHAR';
ftNumeric: Result := 'NUMERIC';
else
if FieldType < 100 then
Result := DefaultSQLType(TFieldType(FieldType))
else
Result := 'VARCHAR'; // fallback
end;
end;
Пример использования
Вот как может выглядеть использование пользовательских типов в реальном коде:
var
FieldDef: TFieldDef;
begin
FieldDef := DataSet.FieldDefs.AddFieldDef;
FieldDef.Name := 'ProductCode';
FieldDef.DataType := ftAlphaNumeric;
FieldDef.Size := 20;
// Или с использованием класса-обертки:
FieldType := TCustomFieldType.CreateExtended(Ord(ftAlphaNumeric));
try
ProcessFieldType(FieldType);
finally
FieldType.Free;
end;
end;
Заключение
Хотя прямое расширение перечисления TFieldType в Delphi невозможно, существует несколько рабочих подходов для добавления пользовательских типов полей. Выбор конкретного решения зависит от ваших требований:
Для простых случаев достаточно констант с offset
Для более сложных сценариев лучше подходят классы-обертки
Для интеграции с базой данных стоит рассмотреть наследование от стандартных классов полей
Каждый из этих методов позволяет эффективно работать с пользовательскими типами данных, сохраняя при этом совместимость с существующим кодом и компонентами Delphi.
Контекст статьи объясняет, как обойти ограничения Delphi для добавления пользовательских типов полей к стандартному перечислению `TFieldType` с помощью альтернативных подходов, таких как классы-обертки, константы или наследование.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.