2011-11-08 21 views
11

In Delphi XE2, sto cercando di sovraccaricare l'operatore in su un record per consentirmi di verificare se il valore rappresentato dal record fa parte di un set. Il mio codice è simile al seguente:Delphi 'in' sovraccarico operatore su un set

type 
    MyEnum = (value1, value2, value3); 
    MySet = set of MyEnum; 
    MyRecord = record 
    Value: MyEnum; 
    class operator In(const A: MyRecord; B: MySet): Boolean; 
    end; 

class operator MyRecord.In(const A: MyRecord; B: MySet): Boolean; 
begin 
    Result := A.Value in B; 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    R: MyRecord; 
    S: MySet; 
begin 
    R.Value := value1; 
    S := [value1, value2]; 
    Button1.Caption := BoolToStr(R in S); 
end; 

Il codice non viene compilato. Per la dichiarazione R in S il compilatore dice: tipi incompatibili MyRecord e MyEnum.

Come posso sovraccaricare l'operatore In su MyRecord in modo che R in S valuterà al True nel codice di cui sopra?

+1

Non penso che ciò che stai cercando di ottenere sia possibile ... dovresti avere più fortuna a scrivere i caratteri extra ".Value" => BoolToStr (R.Value in S); e fatelo con questo – ComputerSaysNo

+0

Il codice nella mia domanda è solo un esempio semplificato. Nella mia applicazione effettiva, il tipo di record non ha una corrispondenza uno-a-uno con il tipo impostato. La soluzione che ho finito per usare era aggiungere una 'funzione InSet (S: MySet): Boolean' al record e usarla invece dell'operatore' in'. –

+0

forse abbastanza buono sarebbe rendere la funzione membro invece - 'BoolToStr (R._in (S));' –

risposta

1

Beh, quasi si può fare questo, ma non si può decidere a. AFAIK, gli operatori di classe lavorano solo sulla classe (o sulla registrazione) in cui sono definiti all'interno, quindi sia R che S nel codice devono essere TMyRecord. Con un po 'l'uso sconsiderato di fusione implicita, otteniamo la seguente:

unit Unit2; 
interface 
type 
    MyEnum = (value1, value2, value3); 
    MySet = set of MyEnum; 
    MyRecord = record 
    Value: MyEnum; 
    ValueSet: MySet; 
    class operator Implicit(A: MyEnum): MyRecord; 
    class operator Implicit(A: MySet): MyRecord; 
    class operator In (Left,Right:MyRecord): Boolean; 
    end; 

implementation 

class operator MyRecord.Implicit(A: MyEnum): MyRecord; 
begin 
    Result.Value := A; 
end; 

class operator MyRecord.Implicit(A: MySet): MyRecord; 
begin 
    Result.ValueSet := A; 
end; 

class operator MyRecord.In(Left, Right: MyRecord): Boolean; 
begin 
    Result:= left.Value in Right.ValueSet; 
end; 
end. 

Di seguito sarà ora complile, e anche il lavoro:

procedure TForm1.Button1Click(Sender: TObject); 
var 
    R: MyRecord; 
    S: MyRecord; 
begin 
    R.Value := value1; 
    S := [value1,value2,value3]; 
    Button1.Caption := BoolToStr(R In S,true); 
end; 

Il che, sono sicuro che saremo tutti d'accordo, è molto più elegante di 'BoolToStr (R.Value in S)'. Tuttavia la seguente sarà anche compilare, ma indica il risultato sbagliato:

procedure TForm1.Button1Click(Sender: TObject); 
var 
    R: MyRecord; 
    S: MyRecord; 
begin 
    R.Value := value1; 
    S := [value1,value2,value3]; 
    Button1.Caption := BoolToStr(S In R,true); 
end; 

Così, come ha commentato Dorin, meglio semplicemente noioso, posata vecchio 'BoolToStr (R.Value in S)'. A meno che non venga pagato per riga di codice. E un bonus per la risoluzione dei bug.

+0

Questa particolare domanda era solo un esercizio per capire gli operatori di classe, che non avevo mai usato prima. Il tipo di record nel codice effettivo su cui stavo lavorando è molto più complicato. Gli operatori che definisce consentono al codice che usa quel record di essere molto più semplice. Alcune centinaia di righe di semplici funzioni dell'operatore consentono di rendere molto più leggibili migliaia di righe di codice complesso. –

+0

Scusa, i miei commenti sono stati un po 'faceti. Ammetto che sono molto preso dal nuovo operatore che carica i record e lo sto usando ampiamente. E sono d'accordo sul fatto che rende il codice molto più chiaro in molti casi. Stavo solo prendendo in giro la mia soluzione, dato che è eccessivo in questo caso particolare ;-) – HMcG

+0

"quindi sia R che S nel tuo codice devono essere TMyRecord" questo non è corretto. – Johan

5

Per l'operatore in a, l'operando di destra deve essere del tipo di record poiché è un operatore impostato e non un operatore binario. Nel tuo caso è l'operando di sinistra.

Così il seguente funzionerà:

type 
    MyRecord = record 
    Value: MyEnum; 
    class operator In(const A: MyRecord; const B: MySet): Boolean; 
    end; 

    MyRecord2 = record 
    Value: MySet; 
    class operator In(const A: MyRecord; const B: MyRecord2): Boolean; 
    class operator In(const A: MyEnum; const B: MyRecord2): Boolean; 
    end; 

class operator MyRecord.In(const A: MyRecord; const B: MySet): Boolean; 
begin 
    Result := A.Value in B; 
end; 

class operator MyRecord2.In(const A: MyRecord; const B: MyRecord2): Boolean; 
begin 
    Result := A.Value in B.Value; 
end; 

class operator MyRecord2.In(const A: MyEnum; const B: MyRecord2): Boolean; 
begin 
    Result := A in B.Value; 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    R: MyRecord; 
    R2: MyRecord2; 
begin 
    R.Value := value1; 
    R2.Value := [value1, value2]; 

    if R in R2 then; 
    if value1 in R2 then; 
end;