2011-11-08 8 views
11

En Delphi XE2, intento sobrecargar el operador in en un registro para permitirme verificar si el valor representado por el registro es parte de un conjunto. Mi código se ve así:Sobrecarga del operador Delphi 'in' en un conjunto

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; 

El código no se puede compilar. Para la instrucción R in S el compilador dice: Tipos incompatibles MyRecord y MyEnum.

Como puedo sobrecargar el operador In en MyRecord modo que R in S evaluará a True en el código anterior?

+1

No creo que lo que intentas lograr sea posible ... deberías tener mejor suerte escribiendo los caracteres adicionales ".Value" => BoolToStr (R.Value en S); y termine con eso – ComputerSaysNo

+0

El código en mi pregunta es solo una muestra simplificada. En mi aplicación actual, el tipo de registro no tiene una correspondencia uno-a-uno con el tipo de conjunto. La solución que terminé usando fue agregar una función 'InSet (S: MySet): Boolean' al registro y usar eso en lugar del operador' in'. –

+0

quizás sea lo suficientemente bueno como para hacer que el miembro funcione en su lugar - 'BoolToStr (R._in (S));' –

Respuesta

1

Bueno, se puede casi hacer esto, pero es posible que no desee. AFAIK, los operadores de clase solo trabajan en la clase (o registro) en que están definidos, por lo que tanto R como S en su código tienen que ser TMyRecord. Con un poco de uso imprudente de la conversión implícita, obtenemos lo siguiente:

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. 

El siguiente ahora complile, e incluso trabajar:

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; 

cual, estoy seguro que todos estaremos de acuerdo, es mucho más elegante que 'BoolToStr (R.Value in S)'. Sin embargo, el siguiente también compilará, pero dará un resultado erróneo:

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; 

Así que, como comentó Dorin, mejor tener sólo aburrido, serio viejo 'BoolToStr (R.Value en S)'. A menos, por supuesto, que te paguen por línea de código. Y una ventaja para la corrección de errores.

+0

Esta pregunta en particular fue solo un ejercicio para descubrir operadores de clase, que nunca había usado antes. El tipo de registro en el código real en el que estaba trabajando es mucho más complicado. Los operadores que define permiten que el código que usa ese registro sea mucho más simple. Algunas cientos de líneas de funciones simples de operador permiten que miles de líneas de código complejo sean mucho más legibles. –

+0

Lo siento, mis comentarios fueron algo graciosos. Admito que estoy muy entusiasmado con el nuevo operador que carga registros, y lo estoy usando ampliamente. Y acepto que hace que el código sea mucho más claro en muchos casos. Solo me estaba burlando de mi propia solución, ya que es excesivo en este caso particular ;-) – HMcG

+0

"por lo que tanto R como S en tu código deben ser TMyRecord", esto es incorrecto. – Johan

5

Para que el operador in funcione, el operando correcto debe ser del tipo de registro ya que es un operador establecido y no un operador binario. En tu caso, es el operando de la izquierda.

Así que el siguiente trabajo:

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;