【问题标题】:Using an enum with generics使用带有泛型的枚举
【发布时间】:2012-09-04 22:24:31
【问题描述】:

我正在尝试创建一个通用类,我可以使用一组枚举来启动其中的值。例如:

constructor TManager<TEnum>.Create;
var
  enum: TEnum;
  enumObj: TMyObject;
begin
  fMyObjectList:=  TObjectDictionary<TEnum,TMyObject>.Create([doOwnsValues],10);
  for enum:= Low(TEnum) to High(TEnum) do
  begin
    enumObj:= TMyObject.Create();
    fMyObjectList.Add(enum, enumObj);
  end;
end;

另外,后面的方法会通过枚举值获取对象,例如:

function TManager<TEnum>.Fetch(enum: TEnum): TMyObject;
begin
  fMyObjectList.TryGetValue(enum, Result);
end;

然而,作为通用参数传递,delphi 并不知道 TEnum 将是一个枚举。我可以以某种方式强制执行吗?

【问题讨论】:

  • 旁注:我确信在泛型单元中可能有一个实际的地图数据结构。如果您立即知道,请随时指出。
  • TDictionary 怎么样?
  • 您可以使用 record 约束来限制值类型。像这样:type TManager&lt;TEnum: record&gt; = class ... 然后编译器将阻止接口和类用于TEnum 但是您目前(XE6 及之前)不能在编译时进一步限制值类型。

标签: delphi generics enums


【解决方案1】:

您不能约束一个泛型参数,使得low()high() 可以在泛型类中使用。唯一可用的约束是类或接口约束。

据我所知,该语言没有提供通用方法来枚举通用枚举类型。可能你能做的最好的事情就是使用 RTTI,牺牲编译时类型的安全性(如 Tobias 所示)。


阅读了 Tobias 的回答后,您似乎真正想要的是 TObjectDictionary&lt;TEnum,TMyObject&gt;。那是因为您希望能够在给定 TEnum 键的情况下找到 TMyObject 实例。你想要TObjectDictionary 而不是TDictionary,因为前者接管了TMyObject 实例的所有权。完成后您需要有人将它们全部释放,您不妨让TObjectDictionary为您做。

有关所有权方面的示例,请参阅@RRUZ 在此处的回答:Example for using Generics.Collections.TObjectDictionary

【讨论】:

    【解决方案2】:

    正如 David 所说,您能做的最好的事情就是在运行时使用 RTTI。

        type  
          TRttiHelp = record
            class procedure EnumIter<TEnum {:enum}>; static;
          end;
    
        class procedure TRttiHelp.EnumIter<TEnum {:enum}>;
        var
          typeInf: PTypeInfo;
          typeData: PTypeData;
          iterValue: Integer;
        begin
          typeInf := PTypeInfo(TypeInfo(TEnum));
          if typeInf^.Kind <> tkEnumeration then
            raise EInvalidCast.CreateRes(@SInvalidCast);
    
          typeData := GetTypeData(typeInf);
          for iterValue := typeData.MinValue to typeData.MaxValue do
            WhateverYouWish;
        end;  
    

    虽然我不知道当你的枚举定义了值时代码的行为,例如

        (a=9, b=19, c=25)
    

    编辑:

    如果您想将iterValue 返回到枚举,您可以使用以下函数,取自enum helper class by Jim Ferguson

    class function TRttiHelp.EnumValue<TEnum {:enum}>(const aValue: Integer): TEnum;
    var
      typeInf: PTypeInfo;
    begin
      typeInf := PTypeInfo(TypeInfo(TEnum));
      if typeInf^.Kind <> tkEnumeration then
        raise EInvalidCast.CreateRes(@SInvalidCast);
    
      case GetTypeData(typeInf)^.OrdType of
        otUByte, otSByte:
          PByte(@Result)^ := aValue;
        otUWord, otSWord:
          PWord(@Result)^ := aValue;
        otULong, otSLong:
          PInteger(@Result)^ := aValue;
      else
        raise EInvalidCast.CreateRes(@SInvalidCast);
      end;
    end;
    

    然后,您可以在构造函数中使用一般提供的作为字典的索引。

    【讨论】:

    • 枚举是连续的,因此您将获得 9 到 25 之间的所有序数值。不确定它是否从 0 开始。不要想。 +1
    • 我记得像 (a=9) 这样定义值的枚举根本没有 RTTI 信息
    • 已检查。 type TTestEnum = (one, two, five = 5);-> [DCC Error] Project4.dpr(14): E2134 Type 'TTestEnum' has no type info德尔福2010
    • 我对 RTTI 不熟悉,对 Delphi 来说总体来说还很陌生。几个问题: 1. 我假设{:enum} 只是一个评论,以澄清你应该发送一个枚举类型? 2.什么是^操作符?
    • 所以,稍后,在像getObject(enum:TEnum):TMyObject 这样的函数中,我将需要再次完成整个交易以获取枚举整数?
    猜你喜欢
    • 1970-01-01
    • 2020-09-06
    • 1970-01-01
    • 1970-01-01
    • 2019-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多