Как сгруппировать постоянные строки вместе в Delphi

I приложение, которое использует строки для другого состояния, которое элемент может быть в течение его жизни.

т

ОТКРЫТЬ, ACTIVE, ЗАКРЫТО, DELETE,

и т.д., в настоящий момент все они жестко закодированы в код так

MyVar := 'OPEN';

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

MyVar := STATUS_OPEN;

но я хотел бы сгруппировать их в одну структуру данных, например,

MyVar := TStatus.Open;

Каков наилучший способ сделать это в Delphi 2007?

Я знаю, что могу сделать запись для этого, но как я могу заполнить его значениями, чтобы он был доступен для всех объектов в системе без необходимости создавать переменную и заполнять значения каждый раз?

Идеально я хотел бы иметь одно центральное место для структуры и значений данных и иметь их легкодоступными (например, TStatus.Open) без необходимости присваивать их переменной или создавать объект каждый раз, когда я его использую.

Я уверен, что есть простое решение, которое я просто пропустил. любые идеи?

Ответ 1

Как сказал Джим, вы можете использовать константы класса или перечисляемый тип:

type
  TItemStatus = (isOpen, isActive, isClosed);
const
  ItemStatusStrings: array[TItemStatus] of string = ('Open', 'Active', 'Closed');

Ответ 2

См. http://edn.embarcadero.com/article/34324 ( "Новые функции языка Delphi с Delphi 7".

Константа класса будет хорошо. Из этой ссылки выше:

type
    TClassWithConstant = class
      public 
        const SomeConst = 'This is a class constant';
    end;


 procedure TForm1.FormCreate(Sender: TObject);
 begin
   ShowMessage(TClassWithConstant.SomeConst);
 end;

Ответ 3

Я лично использовал подход TOndrej широко на нескольких крупных критически важных платформах обработки данных. Преимущество перечислений состоит в том, что их можно легко переносить внутри приложения, они очень компактны (порядковый тип), отлично работают в случае утверждений и полностью безопасны по типу. Более поздняя точка важна для обслуживания, так как удаление или изменение значений перечисления вызовет ошибку компиляции (хорошая идея IMHO).

Некоторые из них могут найти такой подход:

  • Изменение объявленного порядка значений перечисления приведет к foo bar массиву поиска перечисления → string.

  • Если вы используете перечисление в операторах case (хорошая функция), обязательно учитывайте добавленные значения. Я обычно добавляю else к делу и выдаю исключение из неизвестных значений. Гораздо лучше, чем проваливаться в этом случае.

Если вы очень обеспокоены первой ошибкой, вы можете использовать запись в массиве поиска и включить значение перечисления в каждую запись и проверить порядок от инициализации устройства. Это спасло мой бекон много раз в критически важных системах, где частое обслуживание. Этот подход также может использоваться для добавления дополнительных метаданных к каждому значению (очень удобная функция).

Удачи!

unit Unit1;

interface

type
  TItemStatusEnum = (isOpen, isActive, isClosed);

  TItemStatusConst = class
    class function EnumToString(EnumValue : TItemStatusEnum): string;
    property OPEN: string index isOpen read EnumToString;
    property CLOSED: string index isClosed read EnumToString;
    property ACTIVE: string index isActive read EnumToString;
  end;

var
  ItemStatusConst : TItemStatusConst;

implementation

uses
  SysUtils;

type
  TItemStatusRec = record
    Enum  : TItemStatusEnum;
    Value : string;
  end;

const
  ITEM_STATUS_LOOKUP : array[TItemStatusEnum] of TItemStatusRec =
    ((Enum: isOpen;   Value: 'OPEN'),
     (Enum: isActive; Value: 'ACTIVE'),
     (Enum: isClosed; Value: 'CLOSED'));

procedure ValidateStatusLookupOrder;
  var
    Status : TItemStatusEnum;
  begin
    for Status := low(Status) to high(Status) do
      if (ITEM_STATUS_LOOKUP[Status].Enum <> Status) then
        raise Exception.Create('ITEM_STATUS_LOOKUP values out of order!');
  end;

class function TItemStatusConst.EnumToString(EnumValue: TItemStatusEnum): string;
  begin
    Result := ITEM_STATUS_LOOKUP[EnumValue].Value;
  end;

initialization
  ValidateStatusLookupOrder;
end.

Update:

Спасибо за критику - вы абсолютно правы в том, что я решал то, что я воспринимал как проблему, лежащую в основе вопроса. Ниже приведен более простой подход, если вы можете жить с ограничением, которое оно должно ТОЛЬКО работать в Delphi 2007 или более поздних версиях (может работать на D2006?). Трудно избавиться от этих ворчащих мыслей о обратной совместимости;)

type
  ItemStatusConst = record
    const OPEN   = 'OPEN';
    const ACTIVE = 'ACTIVE';
    const CLOSED = 'CLOSED';
  end;

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

Sample usage:

    ShowMessage(ItemStatusConst.ACTIVE);
    ShowMessage(ItemStatusConst.CLOSED);

Просто для удовольствия, я также пересмотрел свой более ранний подход, чтобы напрямую рассмотреть исходный вопрос с аналогичным результатом. На этот раз я использовал "имитируемые свойства класса" (см. здесь) с индексом свойств. Это явно сложнее, чем подход к записи, но сохраняет возможность работать с значениями enum, наборами, строками AND, а также может быть расширен для реализации дополнительных функций метаданных там, где это необходимо. Я считаю, что это работает для версий Delphi, начиная с Delphi 5 IIRC.

Примером того, где я использовал эту технику, был синтаксический анализ, который я создал в Delphi для обработки полного данных IBM AFPDS. потоковая грамматика. Эта гибкость - вот почему я люблю работать в Delphi - это похоже на швейцарский армейский нож;)

Ответ 4

Или вы могли бы комбинировать @Jim и @TOndrej

TGlobalConsts = class
type
  TItemStatus = (isOpen, isActive, isClosed);
const
  ItemStatusStrings: array[TItemStatus] of string = ('Open', 'Active', 'Closed');
end;

Хотя вы можете сделать это класс или запись. Хотя если это класс, вы можете добавить функции класса, например @mghie, и вместо этого просто получить значения из массива const. Лично я предпочитаю иметь все строки в массиве const в интерфейсе, а не наперёд в телах функции в реализации strong > .

class function TGlobalConsts.Active: string;
begin
  Result := ItemStatusStrings[itsActive];
end;

class function TGlobalConsts.Open: string;
begin
  Result := ItemStatusStrings[itsOpen];
end;

Есть много способов сделать это, это точно.

Ответ 5

Это то, что вы можете сделать во всех версиях Delphi:

type
  TStatus = class
    class function Active: string;
    class function Open: string;
    ...
  end;

  class function TStatus.Active: string;
  begin
    Result := 'ACTIVE';
  end;

  class function TStatus.Open: string;
  begin
    Result := 'OPEN';
  end;

Вы можете использовать это как хотите:

MyVar := TStatus.Open;

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

Ответ 6

В дополнение к тому, что сказал Тондрей:

Можно также объявить константу записи:

type
  TRecordName = record
    Field1: Integer;
    Field2: String
  end;

const
  itemstatus : TRecordName = (
    Field1: 0;
    Field2: 'valueoffield2'
  );

Также возможен массив записей, объединив синтаксис записи const выше и синтаксис массива, показанный TOndrej.

Однако способ, который я обычно предпочитаю, требует инициализации:

  • заполнение TStringDynArray именами перечислений через RTTI
  • Если затем выяснится, что имена должны быть настраиваемыми, то стойкость (однако, она устроена) может просто загружать/сохранять динамику.

Ответ 7

У вас может быть глобальная переменная типа записи. Тип записи должен иметь поле для каждого вашего статуса.

Вы можете заполнить запись в разделе Инициализировать любого устройства, вызывая процедуру или функцию, объявленные в этом блоке. У вас может быть отдельный блок для выполнения этой работы, например StatusTypes.pas.

В разделе интерфейса вы можете объявить что-то вроде:

type 
  TStatus = record
    OPEN: string;
  end;

var
  Status: TStatus;