Search code examples
delphienumsdelphi-xe7

How to set and check for no-value as enumerate?


I'm converting some of the common values into enumerated values. But I have a problem with those where missing value/no-value also has meaning:

vDataType:string;

If vDataType = '' Then // no datatype assigned, yet -> handle appropriately
else if vDataType = 'PROJECT' then // process as project record
else if vDataTpye = 'GROUP' then // process as project group record
else if vDataType = 'TASK' then // process as project task

If I now set :

TDataType = (dtProject, dtGroup, dtTask);

var vDatatype:TDataType;

I can use

If vDataType = dtProject Then ...
else if vDataType = dtGroup then ...
else if vDataType = dtTaks then ...

but since first element is dtProject as 0, how can I now check for no value, to replace: if vDatatype = '' then...

The records are all in same array, so no-value has meaning as it differentiates records from others that were marked as Project, Group or Task. The records are marked in the process of importing records and I would like to avoid setting one of the enum values, like dtNone, to all records that are not dtProject, dtGroup or dtTask.

EDIT:

Here is example where I got stuck:

TData = record
    LineID:integer;
    FullLine:string;
    //...
    DataType:string;
end;

var vArray:TArray<TData>;

procedure ProcessDataAfterImport;
var i:integer;
begin
  for i:=0 to High(vArray) do
  begin
    if Copy(vArray[i].FullLine,1,4)='PRJ=' then
        vArray[i].DataType := 'PROJECT'
    else if Copy(vArray[i].FullLine,1,4)='GRP=' then
        vArray[i].DataType := 'GROUP'
    else if Copy(vArray[i].FullLine,1,4)='TSK=' then
        vArray[i].DataType := 'TASK';
  end;
end;

procedure ProcessProjects;
var i:integer;    
begin
  for i:=0 to High(vArray) do
  if vArray[i].DataType = 'PROJECT' Then
     ParseProjectRecord(vArray[i]);
end;

And I have lot's of similar methods as ProcessProjects that process records based on DataType and I wanted to not use 'PROJECT' everywhere but enums, which are integers and I assume also faster and not prone to mistakes like 'PROJCET'. I counted 'PROJECT' is used 55 times. 'GROUP' and 'TASK' little less, but then I have other examples that use similar string values to designate record type.


Solution

  • What you appear to be asking for is not possible.

    It is difficult to say without seeing more of the code involved in your case but I suspect that the use of an empty string - whether by accident or design - was exploiting the fact that strings are one of the few types in Delphi that are initialised by the runtime when declared as local variables. This also includes - for example - interface references and dynamic arrays.

    This special treatment of these types for local variables is necessary because the runtime treats these types in special ways that rely on the certainty that variables of these types are in a valid initial state.

    e.g. the Length field in the RTTI of a String must correctly reflect the memory allocated to hold the chars in that string otherwise memory errors will result when that string is modified. An interface must be NIL to avoid an invalid call to Release when initially assigned some other value. etc etc

    Values of other types declared as class member variables, for example, may appear to be initialised in new instance of a class but this is the result of the zero-ing of memory for newly allocated instances of those classes, rather than any actual initialisation as such.

    The problem when changing from a String to an enum is that enums do not require (and therefore do not receive) any such special treatment and so must be initialised explicitly.

    NOTE: Even if enums were auto-initialised they would be initialised to 0 (zero), i.e. the first member of the enum (unless that first member has been explicitly assigned a non-zero ordinal value).

    With that in mind, this means that if enum variables are required to be able to indicate No Value or Not Set then this must be present as a valid value in the enum itself and you must explicitly initialise variables of that type as appropriate.

    NOTE: If you ensure that this Not set value is the first member of the enum then it will, as you have observed, have the value 0 (zero) which means that any class member variables of this type will be automatically "initialised" with this Not set value in new instances of that class.

    But for local variables, record members etc, you will have to initialise it explicitly, just as you would have to initialise an Integer or a Boolean etc etc.