There are two ways to enable a custom variant type to support comparison operators (=, <>, <, <=, >, >=). You can override the Compare method, or you can override the CompareOp method.
The Compare method is easiest 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. For example, the TConvertVariantType object in the VarConv unit implements the following Compare method:
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," however, 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 VarCmplx unit. It supports only a test of equality or inequality:
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 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.
Copyright(C) 2009 Embarcadero Technologies, Inc. All Rights Reserved.
|
What do you think about this topic? Send feedback!
|