【问题标题】:Enumeration Range Checking枚举范围检查
【发布时间】:2022-01-27 20:24:10
【问题描述】:

Delphi 不直接支持将整数类型转换为枚举的任何范围检查或超出范围的异常提升。见:How do I convert an integer to an enumerated type? 我试图通过对枚举子范围索引的数组进行自动范围检查来解决这个问题。在我的测试程序中,我故意使用了非连续枚举,它禁用 RTTI 并为没有命名枚举但可以通过 typecasting、Inc、Dec 等访问的有效子范围值提供测试。

程序按预期处理子范围 (2) 的非枚举元素,但不会为枚举数组生成超出范围的异常 - 但会为等效的整数索引数组生成。这有什么好的理由吗?我可以使用整数索引数组来解决问题,但枚举索引会更健壮一些。

type MyEnum = (zero,one,three=3);
var EnumCheck: array[MyEnum] of integer = (0,1,2,3);
var iEnum: integer;
var MyEnumVar: MyEnum;
var IntArray: array[0..3] of integer = (0,1,2,3);

procedure Test;
begin
{$R+}
  MyEnumVar:= MyEnum (1);         // One
  iEnum := EnumCheck[MyEnumVar];  // OK - iEnum = 1
  MyEnumVar:= MyEnum (2);         // Out-of-bound
  iEnum := EnumCheck[MyEnumVar];  // OK - iEnum = 2
  MyEnumVar:= MyEnum (3);         // Three
  iEnum := EnumCheck[MyEnumVar];  // OK - iEnum = 3
  MyEnumVar:= MyEnum (4);         // Out-of-bound
  iEnum := EnumCheck[MyEnumVar];  // no Exception; iEnum is set to random value
  iEnum := 4;
  iEnum := IntArray[iEnum];      // Exception thrown here
  iEnum := IntArray[4];          // Compiler "subrange" error
end;

使用 Delphi 10.4 更新 2

编辑:我找到了我的工作。将校验数组定义为:

var iCheck: array[Ord(Low(MyEnum))..Ord(High(MyEnum))] of integer = (0,1,2,3);

并按预期抛出超出范围的异常。

【问题讨论】:

  • 我错过了您没有在值 4 上获得异常的部分,因此我删除了我的答案,因为它没有解决您的问题。您可以发布您的解决方法作为答案。
  • @Dalija 我还是很好奇为什么使用枚举索引的数组不会抛出范围检查异常。即使枚举本身没有经过范围检查,我也会认为数组会是。
  • 目前我没有答案。也许这是出于某种更深层原因的预期行为,也许它只是编译器中的一个错误。

标签: delphi casting enumeration range-checking


【解决方案1】:

我已经使用枚举记录助手将我的变通方法实现为一个相对简洁的解决方案。

type MyEnum = (Zero, One, Two, Three);

type MyEnumHelper = record helper for MyEnum
  function SetInRange(const Value: integer): boolean;
end;

function MyEnumHelper.SetInRange(const Value: integer): boolean;
var Check: array[Ord(Low(MyEnum))..Ord(High(MyEnum))] of integer;
begin
  Result := true;
  Self := MyEnum(Value);
{$UNDEF _RANGEOFF}
{$IFOPT R-} {$DEFINE _RANGEOFF} {$R+} {$ENDIF}
  try
    var iCheck := Check[Value];
  except on ERangeError do
    Exit(false);
  end;
{$IFDEF _RANGEOFF} {$R-} {$ENDIF}
end;

// Usage

function SetEnums(const Val1, Val2, Val3: integer): boolean
var MyEnumVar1, MyEnumVar2, MyEnumVar3: MyEnum;
begin
  if (not MyEnumVar1.SetInRange(Val1)) then Exit(false);
  if (not MyEnumVar2.SetInRange(Val2)) then Exit(false);
  if (not MyEnumVar3.SetInRange(Val3)) then Exit(false);
end;

注意。固定长度数组(Check)不需要手动初始化,因为我们只对范围感兴趣。

没有完全可靠的方法可以将其用于非连续枚举(正如我在原始示例中使用的那样),因为“匿名”枚举仍在范围内。而且,如果您要在帮助程序中使用实际的生成值来解决此问题,那么它首先会绕过使用帮助程序的要点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-06
    • 1970-01-01
    • 1970-01-01
    • 2018-08-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多