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