AFAIK没有内置功能.在网上搜索我找到了这个功能,它对我有用,但我不喜欢使用它,因为它是汇编,我无法理解它在做什么.所以我写了这个函数也有效:
function Cardinality(const PSet: PByteArray;
const SizeOfSet(*in bytes*): Integer): Integer;
const
Masks: array[0..7] of Byte = (1, 2, 4, 8, 16, 32, 64, 128);
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;
现在,我想知道我是否可以依赖这个功能?或者也许设置数据类型背后有一个技巧,这就是delphi没有内置方法的原因.
但if
我的功能是可靠的then
,我怎样才能改进它:
将常量传递给它
进行类型检查并确保将一个集传递给该函数
传递值而不是其地址
摆脱SizeOfSet
参数
我想把它称为Cardinality(AnySet)
而不是Cardinality(@AnySet, SizeOf(TAnySet))
.
顺便说一句,我需要在XE和XE5中编译它.
您可以使用泛型和RTTI实现此功能.像这样:
uses SysUtils, TypInfo; type ERuntimeTypeError = class(Exception); TSet= 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 } class function TSet .TypeInfo: PTypeInfo; begin Result := System.TypeInfo(T); end; class function TSet .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 .Cardinality(const Value: T): Integer; var EnumTypeData: PTypeData; begin if not IsSet then raise ERuntimeTypeError.Create('Invalid type in TSet , T must be a set'); Result := GetCardinality(PByteArray(@Value), SizeOf(Value)); end;
用法:
Writeln(TSet.Cardinality(Value));