Enums vs Const vs Class Const в программировании Delphi

У меня есть целочисленное поле в ClientDataSet, и мне нужно сравнить с некоторыми значениями, что-то вроде этого:

Я могу использовать const

const
  mvValue1 = 1;
  mvValue2 = 2;

if ClientDataSet_Field.AsInteger = mvValue1 then

или перечисления

TMyValues = (mvValue1 = 1, mvValue2 = 2);

if ClientDataSet_Field.AsInteger = Integer(mvValue1) then

или класс const

TMyValue = class
const
   Value1 = 1;
   Value2 = 2;
end;

if ClientDataSet_Field.AsInteger = TMyValues.Value1 then

Мне нравится подход класса const, но кажется, что это не путь delphi, поэтому я хочу знать, что вы думаете

Ответ 1

Я бы не сказал, что класс consts не является способом Delphi. Это просто они были введены в Delphi совсем недавно, и многие книги и статьи, которые вы найдете в Интернете, были написаны до их введения, и поэтому вы не увидите их широко используемыми. Многие разработчики Delphi (я бы сказал, большинство) начнут использовать Delphi, прежде чем они станут доступны, и, следовательно, они не являются первым, о чем думают.

Ответ 2

Декларация:

type
  TMyValues = class
    type TMyEnum = (myValue1, myValue2, myValue3, myValue4);
    const MyStrVals: array [TMyEnum] of string =
      ('One', 'Two', 'Three', 'Four');
    const MyIntVals: array [TMyEnum] of integer =
      (1, 2, 3, 4);
  end;

Использование:

if ClientDataSet_Field.AsInteger = TMyValues.MyIntVals[myValue1] then

Литой, как правило, был мой последний выбор.

Ответ 3

Одна вещь, которую следует учитывать, - обратная совместимость. Константы класса относительно новы для Delphi, поэтому, если ваш код должен быть совместим с предыдущими версиями, чем они отсутствуют.

Обычно я использую перечисленные типы, с отличием от вашего, заключается в том, что мое первое перечисление обычно представляет собой элемент undefined для представления NULL или 0 в поле int.

TmyValues = (myvUndefined, myvDescription1, myvDescription2)

if ClientDataSet_Field.AsInteger = Ord(myvDescription1) then...

Чтобы использовать немного ответа Джима МакКета - если вам нужно отобразить пользователю текстовую версию, или если вам нужно преобразовать выделенный текст в нумерованный тип, тогда массив будет полезен в сочетании с Тип:

const MYVALS: array [TmyValues ] of string = ('', 'Description1', 'Description2');

Затем вы можете использовать служебные функции для установки/получения перечисляемого типа в/из строки:

Function MyValString(const pMyVal:TmyValues):string;
begin
  result := MYVALS[Ord(pMyVal)];
end;

Function StringToMyVal(const pMyVal:String):TMyValues;
var i:Integer;
begin
  result := myvUndefined;
  for i := Low(MYVALS) to High(MYVALS) do
  begin
    if SameText(pMyVal, MYVALS[i]) then
    begin
      result := TMyValues(i);
      break;
    end;
  end;
end;

Продолжая... у вас может быть программа рассеяния, чтобы установить поле combo/list:

Procedure SetList(const DestList:TStrings);
begin
  DestList.Clear;
  for i := Low(MYVALS) to High(MYVALS) do
  begin
    DestList.Insert(MYVALS[i]);
  end;
end;

В коде: SetList (Combo1.Items) или SetList (ListBox1.Items)..

Затем, если вы видите шаблон здесь... полезные функции утилиты, связанные с вашим перечислением, то вы добавляете все к своему классу и помещаете этот класс в свою собственную единицу с именем MyValueEnumeration или whaterver. Вы получаете весь код, связанный с этим перечислением, в одном месте и продолжаете добавлять функции утилиты по мере необходимости. Если вы храните устройство в чистоте - не смешивайтесь с другими несвязанными функциями, он останется очень удобным для всех проектов, связанных с этим перечислением.

С течением времени вы увидите больше шаблонов, и вы снова и снова используете одну и ту же функциональность, и вы снова создадите лучшую мышеловку.

Ответ 4

так много вариантов!:-) Я предпочитаю перечисления и регулярно использую их, как вы описываете. одна из частей, которые мне нравятся, заключается в том, что я могу использовать их с циклом "для". Я также использую константы класса, но предпочитаю перечисления (даже частные перечисления) в зависимости от того, чего я пытаюсь достичь.

TMyType=class
private const    // d2007 & later i think
  iMaxItems=1;   // d2007 & later i think
private type     // d2007 & later i think
  TMyValues = (mvValue1 = 1, mvValue2 = 2);     // d2007 & later i think
private
public
end;

Ответ 5

При использовании констант я рекомендую назначить тип, когда тип данных является числовым поплавком.

Delphi и другие языки не всегда будут правильно оценивать значения, если типы не совпадают...

TMyValue = class
const
   // will not compare correctly to float values.
   Value1 = 1; // true constant can be used to supply any data type value
   Value2 = 2; // but should only be compared to similar data type

   // will not compare correctly to a single or double.
   Value3 = 3.3; // default is extended in debugger 

   // will not compare correctly to a single or extended.
   Value1d : double = Value1; // 1.0
   Value2d : double = Value2; // 2.0
end;

Сравнимые значения float в операциях if() и while() должны сравниваться со значениями одного и того же типа данных, поэтому лучше всего определить временную или глобальную переменную типа float, используемую для любых операторов сравнения (= < > ).

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

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

*** Примечание. Извлечение значения из одного типа с плавающей запятой в другое может изменить сохраненное значение из введенного вами для целей сравнения, поэтому убедитесь, что при выполнении этого цикла выполняется цикл unit test.

К сожалению, Delphi не разрешает формат перечисления, как...   TController: Integer = (NoController = 0, ncpod = 1, nextwave = 2);

или ввести имя типа для доступа к значениям перечисления.

или разрешить константу класса использовать в качестве параметра по умолчанию в вызове типа...   function getControllerName (Контроллер: TController = TController.NoController): string;

Однако более защищенный подход, обеспечивающий оба типа доступа, должен состоять в перечислении внутри класса.

 TController = class
  //const
     //NoController : Integer = 1;
     //ncpod : Integer = 2;
     //nextwave : Integer = 3;

  type
     Option = (NoController = 0, ncpod = 1, nextwave = 2);

  public
    Class function Name( Controller : Option = NoController) : string; static;
 end;

 implementation

 class function TController.Name( Controller : Option = NoController) : string;
 begin
    Result := 'CNC';
    if (Controller = Option.nextwave) then
       Result := Result + ' Piranha'
    else if (Controller = Option.ncpod) then
       Result := Result + ' Shark';

    Result := Result + ' Control Panel';
 end;

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

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

 using TControllerUnit;

 function getName( Controller : TController.Option = TController.Option.NoController) : string;

 implementation

 function getName( Controller : TController.Option = TController.Option.NoController) : string;
 begin
   Result := 'CNC';
   if (Controller = TController.Option.nextwave) then
       Result := Result + ' Piranha'
   else if (Controller = TController.Option.ncpod) then
       Result := Result + ' Shark';

   Result := Result + ' Control Panel';
 end;

Ответ 6

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

например.

 Select value, Description from tbl_values inner join tbl_lookup_values where tbl_values.Value = tbl_lookup_values.value

if ClientDataSet_Field.AsString = 'ValueIwant' then