2015-12-23 18 views
8

AFAIK gibt es keine eingebaute Funktion dafür. Suche im Internet fand ich diese function und es funktioniert für mich, aber ich bevorzuge es nicht zu verwenden, da es Montage ist und ich kann nicht verstehen, was es tut. Also schrieb ich diese Funktion, die auch funktioniert:Wie kann ich die Anzahl der Elemente einer beliebigen Variablen vom Typ festlegen?

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; 

Jetzt will ich wissen, ob ich auf diese Funktion verlassen können? Oder vielleicht gibt es einen Trick hinter dem eingestellten Datentyp und deshalb hat Delphi keine eingebaute Methode dafür.

Aber if meine Funktion ist zuverlässig then wie kann ich verbessern, es zu:

  1. Pass Konstanten es
  2. eine Typprüfung durchführen und sicherstellen, dass ein Satz an die Funktion
  3. geben wird
  4. Pass der Wert statt dessen Adresse
  5. Get
  6. von SizeOfSet Parameter los

Ich möchte es wie Cardinality(AnySet) anstelle von Cardinality(@AnySet, SizeOf(TAnySet)) nennen.

Übrigens, ich muss dies in XE und XE5 kompilieren.

Antwort

5

Sie können dies mit Generika und RTTI implementieren. Wie so:

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; 

Verbrauch:

Writeln(TSet<SomeSet>.Cardinality(Value)); 
+0

Danke, aber XE nicht kompilieren: ** E2506 Die im Interface-Abschnitt deklarierte Methode des parametrisierten Typs darf kein lokales Symbol 'GetCardinality' ** verwenden. Also muss ich 'GetCardinality' als Klassenfunktion definieren. Dasselbe Problem mit 'Masken'. Nach diesen Änderungen läuft es und funktioniert gut. – saastn

+1

Ja, das ist ein Compilerfehler in älteren Compilern. Sie können diese Funktion manuell inline einfügen, wenn Sie möchten. Wie auch immer, das Konzept ist solide. –

2

In früheren Version von Delphi können Sie dies tun:

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

Sind 'TSet' und' TSetElement' allgemeine Typen oder Typen, die Sie in Ihrem Code definiert haben? – saastn