Implementing Comparison Operations
Go Up to Creating a Class to Enable the Custom Variant Type
There are two ways to enable a custom variant type to support comparison operators (=, <>, <, <=, >, >=). You can either override the Compare
method, or you can override the CompareOp
method.
The Compare method is easier if your custom variant type supports the full range of comparison operators. Compare
takes three parameters: the left-hand operand, the right-hand operand, and a var Parameter that returns the relationship between the two.
TConvertVariantType
object in the VarConv unit implements the following Compare
method:
Delphi:
procedure TConvertVariantType.Compare(const Left, Right: TVarData;
var Relationship: TVarCompareResult);
const
CRelationshipToRelationship: array [TValueRelationship] of TVarCompareResult =
(crLessThan, crEqual, crGreaterThan);
var
LValue: Double;
LType: TConvType;
LRelationship: TValueRelationship;
begin
// supports...
// convvar cmp number
// Compare the value of convvar and the given number
// convvar1 cmp convvar2
// Compare after converting convvar2 to convvar1's unit type
// The right can also be a string. If the string has unit info then it is
// treated like a varConvert else it is treated as a double
LRelationship := EqualsValue;
case Right.VType of
varString:
if TryStrToConvUnit(Variant(Right), LValue, LType) then
if LType = CIllegalConvType then
LRelationship := CompareValue(TConvertVarData(Left).VValue, LValue)
else
LRelationship := ConvUnitCompareValue(TConvertVarData(Left).VValue,
TConvertVarData(Left).VConvType, LValue, LType)
else
RaiseCastError;
varDouble:
LRelationship := CompareValue(TConvertVarData(Left).VValue, TVarData(Right).VDouble);
else
if Left.VType = VarType then
LRelationship := ConvUnitCompareValue(TConvertVarData(Left).VValue,
TConvertVarData(Left).VConvType, TConvertVarData(Right).VValue,
TConvertVarData(Right).VConvType)
else
RaiseInvalidOp;
end;
Relationship := CRelationshipToRelationship[LRelationship];
end;
If the custom type does not support the concept of "greater than" or "less than," only "equal" or "not equal," it is difficult to implement the Compare
method, because Compare
must return crLessThan
, crEqual
, or crGreaterThan
. When the only valid response is "not equal," it is impossible to know whether to return crLessThan
or crGreaterThan
.
Thus, for types that do not support the concept of ordering, you can override the CompareOp method instead.
CompareOp has three parameters: the value of the left-hand operand, the value of the right-hand operand, and the comparison operator. Implement this method to perform the operation and return a boolean that indicates whether the comparison is True
. You can then call the RaiseInvalidOp method when the comparison makes no sense.
For example, the following CompareOp
method comes from the TComplexVariantType
object in the System.VarCmplx unit. It supports only a test of equality or inequality:
Delphi:
function TComplexVariantType.CompareOp(const Left, Right: TVarData;
const Operator: Integer): Boolean;
begin
Result := False;
if (Left.VType = VarType) and (Right.VType = VarType) then
case Operator of
opCmpEQ:
Result := TComplexVarData(Left).VComplex.Equal(TComplexVarData(Right).VComplex);
opCmpNE:
Result := not TComplexVarData(Left).VComplex.Equal(TComplexVarData(Right).VComplex);
else
RaiseInvalidOp;
end
else
RaiseInvalidOp;
end;
Note that the types of operands that both these implementations support are very limited. As with Implementing Binary Operations, you can use the RightPromotion
and LeftPromotion
methods to limit the cases you must consider by forcing a cast before Compare
or CompareOp
is called.