AFAIK没有内置功能.在网上搜索我发现这个
function并且它适用于我,但我不喜欢使用它,因为它是汇编而我无法理解它在做什么.所以我写了这个函数也有效:
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;
现在,我想知道我是否可以依赖这个功能?或者也许设置数据类型背后有一个技巧,这就是德尔福没有内置方法的原因.
但如果我的功能可靠,那么我该如何改进它:
>将常量传递给它
>进行类型检查并确保将一个集传递给该函数
>传递值而不是其地址
>摆脱SizeOfSet参数
我想把它称为Cardinality(AnySet)而不是Cardinality(@AnySet,SizeOf(TAnySet)).
顺便说一句,我需要在XE和XE5中编译它.
解决方法@H_301_19@
您可以使用泛型和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,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;
用法:
Writeln(TSet<SomeSet>.Cardinality(Value));
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,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;
用法:
Writeln(TSet<SomeSet>.Cardinality(Value));