Enums vs Const vs Class Const in Delphi programming

柔情痞子 提交于 2020-01-12 13:46:34

问题


I have an integer field in a ClientDataSet and I need to compare to some values, something like this:

I can use const

const
  mvValue1 = 1;
  mvValue2 = 2;

if ClientDataSet_Field.AsInteger = mvValue1 then

or enums

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

if ClientDataSet_Field.AsInteger = Integer(mvValue1) then

or class const

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

if ClientDataSet_Field.AsInteger = TMyValues.Value1 then

I like the class const approach but it seems that is not the delphi way, So I want to know what do you think


回答1:


I wouldn't say that class consts are not the Delphi way. It's just they have been introduced to Delphi quite recently, and a lot of books and articles you'll find on the internet were written before their introduction, and thus you won't see them widely used. Many Delphi developers (I'd say the majority) will have started using Delphi before they were made available, and thus they're not the first thing that one thinks about.




回答2:


Declaration:

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;

Usage:

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

A cast would generally be my last choice.




回答3:


One thing to consider is backwards compatibility - class constants are relatively new to Delphi so if your code has to be sharable with previous versions than they are out.

I typically use enumerated types, with the difference from yours is that my first enumeration is usually an 'undefined' item to represent NULL or 0 in an int field.

TmyValues = (myvUndefined, myvDescription1, myvDescription2)

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

To use a little bit of Jim McKeeth's answer - if you need to display to the user a text viewable version, or if you need to convert their selected text into the enumerated type, then an array comes in handy in conjuction with the type:

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

You can then have utility functions to set/get the enumerated type to/from a string:

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;

Continuing on... you can have scatter routine to set a combo/list box:

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

In code: SetList(Combo1.Items) or SetList(ListBox1.Items)..

Then if you are seeing the pattern here... useful utility functions surrounding your enumeration, then you add everything to it's own class and put this class into it's own unit named MyValueEnumeration or whaterver. You end up with all the code surrounding this enumeration in one place and keep adding the utility functions as you need them. If you keep the unit clean - don't mix in other unrelated functionality then it will stay very handy for all projects related to that enumeration.

You'll see more patterns as time goes and you use the same functionality over and over again and you'll build a better mousetrap again.




回答4:


so many options! :-) i prefer enums and routinely use them as you describe. one of the parts i like is that i can use them with a "for" loop. i do use class constants as well but prefer enums (even private enums) depending on what i'm trying to achieve.

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:


When using constants I recommend assigning the type when the data type is a numeric float.

Delphi and other languages will not always evaluate values correctly if the types do not match...

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;

Compared float values in if () and while () statements should be compared to values of the same data type, so it is best to define a temporary or global variable of the float type used for any comparison statements (=<>).

When compared to the same float data type this format is more reliable for comparison operators in any programming language, not just in Delphi, but in any programming language where the defined float types vary from variable to constant.

Once you assign a type, Delphi will not allow you to use the variable to feed another constant, so true constants are good to feed any related data type, but not for comparison in loops and if statements, unless they are assigned and compared to integer values.

***Note: Casting a value from one float type to another may alter the stored value from what you entered for comparison purposes, so verify with a unit test that loops when doing this.

It is unfortunate that Delphi doesn't allow an enumeration format like... TController : Integer = (NoController = 0, ncpod = 1, nextwave = 2);

or enforce the type name for access to the enumeration values.

or allow a class constant to be used as a parameter default in a call like... function getControllerName( Controller : TController = TController.NoController) : string;

However, a more guarded approach that provides both types of access would be to place the enumeration inside a class.

 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;

This approach will effectively isolate the values, provide the static approach and allow access to the values using a for () loop.

The access to the values from a floating function would be like this...

 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:


An option you haven't thought of is to use a lookup table in the database and then you can check against the string in the database.

eg.

 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


来源:https://stackoverflow.com/questions/382234/enums-vs-const-vs-class-const-in-delphi-programming

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!