Ответ 1
Как сказал Джим, вы можете использовать константы класса или перечисляемый тип:
type
TItemStatus = (isOpen, isActive, isClosed);
const
ItemStatusStrings: array[TItemStatus] of string = ('Open', 'Active', 'Closed');
I приложение, которое использует строки для другого состояния, которое элемент может быть в течение его жизни.
т
ОТКРЫТЬ, ACTIVE, ЗАКРЫТО, DELETE,
и т.д., в настоящий момент все они жестко закодированы в код так
MyVar := 'OPEN';
Я работаю над изменением этого, поскольку это может быть проблемой обслуживания, поэтому я хочу изменить их все на константы, я собирался сделать это так
MyVar := STATUS_OPEN;
но я хотел бы сгруппировать их в одну структуру данных, например,
MyVar := TStatus.Open;
Каков наилучший способ сделать это в Delphi 2007?
Я знаю, что могу сделать запись для этого, но как я могу заполнить его значениями, чтобы он был доступен для всех объектов в системе без необходимости создавать переменную и заполнять значения каждый раз?
Идеально я хотел бы иметь одно центральное место для структуры и значений данных и иметь их легкодоступными (например, TStatus.Open) без необходимости присваивать их переменной или создавать объект каждый раз, когда я его использую.
Я уверен, что есть простое решение, которое я просто пропустил. любые идеи?
Как сказал Джим, вы можете использовать константы класса или перечисляемый тип:
type
TItemStatus = (isOpen, isActive, isClosed);
const
ItemStatusStrings: array[TItemStatus] of string = ('Open', 'Active', 'Closed');
См. 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;
Я лично использовал подход 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 - это похоже на швейцарский армейский нож;)
Или вы могли бы комбинировать @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;
Есть много способов сделать это, это точно.
Это то, что вы можете сделать во всех версиях 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 не всегда необходимы, чтобы делать такие вещи...
В дополнение к тому, что сказал Тондрей:
Можно также объявить константу записи:
type
TRecordName = record
Field1: Integer;
Field2: String
end;
const
itemstatus : TRecordName = (
Field1: 0;
Field2: 'valueoffield2'
);
Также возможен массив записей, объединив синтаксис записи const
выше и синтаксис массива, показанный TOndrej.
Однако способ, который я обычно предпочитаю, требует инициализации:
TStringDynArray
именами перечислений через RTTIУ вас может быть глобальная переменная типа записи. Тип записи должен иметь поле для каждого вашего статуса.
Вы можете заполнить запись в разделе Инициализировать любого устройства, вызывая процедуру или функцию, объявленные в этом блоке. У вас может быть отдельный блок для выполнения этой работы, например StatusTypes.pas.
В разделе интерфейса вы можете объявить что-то вроде:
type
TStatus = record
OPEN: string;
end;
var
Status: TStatus;