Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Условные типы атрибутов перечислений в Delphi: способы доступа и их эффективность

Delphi , Компоненты и Классы , Коллекции

 

В Delphi перечисления (enums) являются мощным инструментом для работы с константами, которые могут быть легко именованы. Однако, иногда возникает необходимость ассоциировать с каждым элементом перечисления дополнительные данные, такие как атрибуты. В этой статье мы рассмотрим, как это можно сделать и какие методы доступа к этим атрибутам существуют.

Введение в атрибуты перечислений

Атрибуты перечислений позволяют связывать дополнительную информацию с каждым элементом перечисления. Это может быть полезно, например, для хранения метаданных, таких как имена строковых значений, которые могут быть использованы для отображения или для других целей. В Delphi атрибуты можно использовать для аннотирования элементов перечисления, но из-за ограничений в RTTI (Run-Time Type Information) доступ к атрибутам конкретных элементов может быть ограничен.

Пример использования атрибутов перечислений

Рассмотрим пример перечисления с атрибутами:

type
  TOAuthSubjectTypes = (
    [MappingAttr('public')]
    ostPublic,
    [MappingAttr('pairwise')]
    ostPairwise
  );

Здесь атрибут MappingAttr ассоциируется с каждым элементом перечисления. Мы хотим получить доступ к этому атрибуту для конкретного элемента, например, для ostPairwise.

Ограничения доступа к атрибутам

В Delphi RTTI не хранит информацию об атрибутах конкретных элементов перечисления. RTTI предоставляет информацию о типах и их полях, но не о конкретных элементах перечислений. Это означает, что стандартный способ доступа к атрибутам через RTTI не работает для отдельных элементов перечисления.

Решение через атрибуты типа перечисления

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

Пример:

type
  TOAuthSubjectTypes = (
    ostPublic,
    ostPairwise
  );

  [MappingAttr('public')]
  TOAuthSubjectTypes;
  [MappingAttr('pairwise')]
  TOAuthSubjectTypes;

Затем можно использовать RTTI для доступа к атрибутам по индексу элемента перечисления.

Реализация доступа к атрибутам

Для реализации доступа к атрибутам можно использовать следующие функции:

  1. EnumToString: Преобразует значение перечисления в строку, связанную с атрибутом.

  2. StringToEnum: Преобразует строку в значение перечисления на основе связанного атрибута.

Пример кода

Вот пример реализации этих функций:

type
  TEnumMapping = class
  public
    class function EnumToString<T>(const Value: T): string; static;
    class function StringToEnum<T>(const Value: string): T; static;
  end;

class function TEnumMapping.EnumToString<T>(const Value: T): string;
var
  RTTICtx: TRttiContext;
  RTTIType: TRttiType;
  RTTIEnum: TRTTIEnumerationType;
  GenericValue: TValue;
  OrdValue: Int64;
begin
  RTTICtx := TRTTIContext.Create;
  try
    RTTIType := RTTICtx.GetType(TypeInfo(T));
    if (RTTIType = nil) or (RTTIType.TypeKind <> tkEnumeration) then
      raise Exception.CreateFmt('Type %s is not an enumeration', [RTTIType.Name]);

    GenericValue := TValue.From<T>(Value);
    if not GenericValue.TryAsOrdinal(OrdValue) then
      raise EInvalidCast.Create('Could not cast generic value to ordinal');

    RTTIEnum := TRTTIEnumerationType(RTTIType);
    if (OrdValue > RTTIEnum.MaxValue) or (OrdValue < RTTIEnum.MinValue) then
      raise EEnumNameNotFound.CreateFmt('%d has no valid enum name for %s', [OrdValue, RTTIType.Name]);

    exit(MappingAttr(RTTIEnum.GetAttributes()[OrdValue]).Value);
  finally
    RTTICtx.Free;
  end;
end;

class function TEnumMapping.StringToEnum<T>(const Value: string): T;
var
  RTTICtx: TRttiContext;
  RTTIType: TRttiType;
  Index: Integer;
  Found: Boolean;
begin
  RTTICtx := TRTTIContext.Create;
  try
    RTTIType := RTTICtx.GetType(TypeInfo(T));
    if (RTTIType = nil) or (RTTIType.TypeKind <> tkEnumeration) then
      raise Exception.CreateFmt('Type %s is not an enumeration', [RTTIType.Name]);

    FillChar(Result, SizeOf(Result), 0);

    var ItemCount, AttrCount: Integer;
    ItemCount := Length(TRTTIEnumerationType(RTTIType).GetNames());
    AttrCount := Length(RTTIType.GetAttributes());

    if (AttrCount = 0) or (ItemCount <> AttrCount) then
      raise ERTTIMappingBadFormat.CreateFmt('RTTI Mapping in %s is badly formated.', [RTTIType.Name]);

    Index := 0;
    Found := False;
    for var Attr: TCustomAttribute in RTTIType.GetAttributes do
    begin
      if Attr is MappingAttr then
      begin
        Inc(Index);
        Found := MappingAttr(Attr).Value = Value;
        if Found then
          break;
      end;
    end;

    if not Found then
      raise ERTTIMappingNotFound.CreateFmt('No match for %s on %s', [Value, RTTIType.Name]);

    PInteger(@Result)^ := Index;
    Exit(Result);
  finally
    RTTICtx.Free;
  end;
end;

Альтернативные решения

Если использование RTTI кажется слишком сложным, можно рассмотреть альтернативные подходы:

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

  2. Использование словарей: Можно использовать словарь (Dictionary) для хранения атрибутов, где ключом будет значение перечисления, а значением — атрибут.

Заключение

Доступ к атрибутам перечислений в Delphi имеет свои ограничения из-за отсутствия RTTI для отдельных элементов. Однако, используя атрибуты, связанные с типом перечисления, и соответствующие функции, можно эффективно работать с этими атрибутами. Альтернативные подходы, такие как использование структур данных или словарей, могут быть полезны в ситуациях, где RTTI не является оптимальным решением.

Создано по материалам из источника по ссылке.

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


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: Коллекции ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-12-22 20:14:06
2025-03-22 19:22:16/0.0036180019378662/0