Delphi 如何获取任何类型set变量的元素数?

Delphi 如何获取任何类型set变量的元素数?,delphi,set,delphi-xe,delphi-xe5,Delphi,Set,Delphi Xe,Delphi Xe5,抱歉,没有内置的功能。在网上搜索时我发现了这个,它对我很有用,但我不喜欢使用它,因为它是组装的,我不知道它在做什么。所以我编写了这个函数,它也可以工作: 函数基数(常量PSet:pbyterarray; const SizeOfSet(*字节*):整数:整数; 常数 掩码:字节=(1,2,4,8,16,32,64,128)的数组[0..7]; 变量 一、 J:整数; 开始 结果:=0; 对于I:=0到SizeOfSet-1 do 对于J:=0到7 do 如果(PSet^[I]和掩码[J])>0,

抱歉,没有内置的功能。在网上搜索时我发现了这个,它对我很有用,但我不喜欢使用它,因为它是组装的,我不知道它在做什么。所以我编写了这个函数,它也可以工作:

函数基数(常量PSet:pbyterarray;
const SizeOfSet(*字节*):整数:整数;
常数
掩码:字节=(1,2,4,8,16,32,64,128)的数组[0..7];
变量
一、 J:整数;
开始
结果:=0;
对于I:=0到SizeOfSet-1 do
对于J:=0到7 do
如果(PSet^[I]和掩码[J])>0,则
公司(结果);
结束;
现在,我想知道我是否可以依赖这个函数?或者,在设置数据类型的背后可能有一个技巧,这就是为什么delphi没有一个内置的方法

但是
如果
我的功能是可靠的
那么
我如何改进它以:

  • 将常量传递给它
  • 执行类型检查并确保将集合传递给函数
  • 传递值而不是其地址
  • 去掉
    SizeOfSet
    参数
  • 我想像
    Cardinality(AnySet)
    一样调用它,而不是
    Cardinality(@AnySet,SizeOf(TAnySet))


    顺便说一句,我需要在XE和XE5中编译它。

    您可以使用泛型和RTTI实现它。像这样:

    uses
      SysUtils, TypInfo;
    
    type
      ERuntimeTypeError = class(Exception);
    
      TSet<T> = class
      strict private
        class function TypeInfo: PTypeInfo; inline; static;
      public
        class function IsSet: Boolean; static;
        class function Cardinality(const Value: T): Integer; static;
      end;
    
    const
      Masks: array[0..7] of Byte = (1, 2, 4, 8, 16, 32, 64, 128);
    
    implementation
    
    { TSet<T> }
    
    class function TSet<T>.TypeInfo: PTypeInfo;
    begin
      Result := System.TypeInfo(T);
    end;
    
    class function TSet<T>.IsSet: Boolean;
    begin
      Result := TypeInfo.Kind=tkSet;
    end;
    
    function GetCardinality(const PSet: PByteArray;
      const SizeOfSet(*in bytes*): Integer): Integer; inline;
    var
      I, J: Integer;
    begin
      Result := 0;
      for I := 0 to SizeOfSet - 1 do
        for J := 0 to 7 do
          if (PSet^[I] and Masks[J]) > 0 then
            Inc(Result);
    end;
    
    class function TSet<T>.Cardinality(const Value: T): Integer;
    var
      EnumTypeData: PTypeData;
    begin
      if not IsSet then
        raise ERuntimeTypeError.Create('Invalid type in TSet<T>, T must be a set');
      Result := GetCardinality(PByteArray(@Value), SizeOf(Value));
    end;
    
    使用
    SysUtils,TypInfo;
    类型
    ERuntimeTypeError=类(异常);
    TSet=类
    严格保密
    类函数TypeInfo:PTypeInfo;内联;静止的
    公众的
    类函数集:布尔;静止的
    类函数基数(常量值:T):整数;静止的
    结束;
    常数
    掩码:字节=(1,2,4,8,16,32,64,128)的数组[0..7];
    实施
    {TSet}
    类函数TSet.TypeInfo:PTypeInfo;
    开始
    结果:=系统类型信息(T);
    结束;
    类函数TSet.IsSet:Boolean;
    开始
    结果:=TypeInfo.Kind=tkSet;
    结束;
    函数GetCardinality(const PSet:PByteArray;
    const SizeOfSet(*字节*):整数:整数;内联;
    变量
    一、 J:整数;
    开始
    结果:=0;
    对于I:=0到SizeOfSet-1 do
    对于J:=0到7 do
    如果(PSet^[I]和掩码[J])>0,则
    公司(结果);
    结束;
    类函数TSet.Cardinality(常量值:T):整数;
    变量
    EnumTypeData:PTypeData;
    开始
    如果不是这样的话
    raise ERuntimeTypeError.Create('TSet中的类型无效,T必须是一个集合');
    结果:=GetCardinality(PByteArray(@Value),SizeOf(Value));
    结束;
    
    用法:

    Writeln(TSet<SomeSet>.Cardinality(Value));
    
    Writeln(t.基数(值));
    
    在早期版本的Delphi中,您可以执行以下操作:

    function Card(ASet: TSet): Integer;
    var
      k: TSetElement;
    begin
      Result := 0;
      for k in ASet do Inc(Result);
    end;
    

    谢谢,但XE没有编译它:接口部分中声明的参数化类型的E2506方法不能使用本地符号“GetCardinality”。因此,我必须将
    GetCardinality
    定义为一个类函数。关于
    掩码
    也有同样的问题。经过这些修改后,它运行正常。是的,这是旧编译器的一个缺陷。如果需要,可以手动内联该函数。无论如何,这个概念是正确的。是
    TSet
    TSetElement
    常规类型还是您在代码中定义的类型?它们是自己的类型:
    type TSetElement=(CardS,CardD,CardC,CardH)//枚举器TSet=TsetElement的集合“它是程序集”-不,它不是程序集。正确使用计算机内存。