Supporting Properties and Methods in Custom Variants
Go Up to Defining Custom Variants Index
Some variants have properties and methods. For example, when the value of a variant is an interface, you can use the variant to read or write the values of properties on that interface and call its methods. Even if your custom variant type does not represent an interface, you may want to give it properties and methods that an application can use in the same way.
Using TInvokeableVariantType
To provide support for properties and methods, the class you create to enable the new custom variant type should descend from System.Variants.TInvokeableVariantType instead of directly from TCustomVariantType.
TInvokeableVariantType defines four methods:
that you can implement to support properties and methods on your custom variant type.
For example, the System.VarConv unit uses TInvokeableVariantType as the base class for TConvertVariantType so that the resulting custom variants can support properties. The following example shows the property getter for these properties:
Delphi:
function TConvertVariantType.GetProperty(var Dest: TVarData;
const V: TVarData; const Name: String): Boolean;
var
LType: TConvType;
begin
// supports...
// 'Value'
// 'Type'
// 'TypeName'
// 'Family'
// 'FamilyName'
// 'As[Type]'
Result := True;
if Name = 'VALUE' then
Variant(Dest) := TConvertVarData(V).VValue
else if Name = 'TYPE' then
Variant(Dest) := TConvertVarData(V).VConvType
else if Name = 'TYPENAME' then
Variant(Dest) := ConvTypeToDescription(TConvertVarData(V).VConvType)
else if Name = 'FAMILY' then
Variant(Dest) := ConvTypeToFamily(TConvertVarData(V).VConvType)
else if Name = 'FAMILYNAME' then
Variant(Dest) := ConvFamilyToDescription(ConvTypeToFamily(TConvertVarData(V).VConvType))
else if System.Copy(Name, 1, 2) = 'AS' then
begin
if DescriptionToConvType(ConvTypeToFamily(TConvertVarData(V).VConvType), System.Copy(Name, 3, MaxInt), LType) then
VarConvertCreateInto(Variant(Dest), Convert(TConvertVarData(V).VValue, TConvertVarData(V).VConvType, LType), LType)
else
Result := False;
end
else
Result := False;
end;
The GetProperty method checks the Name
parameter to determine what property is wanted. It then retrieves the information from the TVarData record of the Variant (V
), and returns it as a Variant (Dest
). Note that this method supports properties whose names are dynamically generated at run time (As[Type]), based on the current value of the custom variant.
Similarly, the SetProperty, DoFunction, and DoProcedure methods are sufficiently generic that you can dynamically generate method names, or respond to variable numbers and types of parameters.
Using TPublishableVariantType
If the custom variant type stores its data using an object instance, then there is an easier way to implement properties, as long as they are also properties of the object that represents the data of the variant. If you use System.TypInfo.TPublishableVariantType as the base class for your custom variant type, then you need only implement the GetInstance method, and all the published properties of the object that represents the data of the variant are automatically implemented for the custom variants.
For example, as was seen in Storing a custom variant type's data, TComplexVariantType stores the data of a complex-valued variant using an instance of TComplexData. TComplexData has a number of published properties (Real, Imaginary, Radius, Theta, and FixedTheta) that provide information about the complex value. TComplexVariantType descends from TPublishableVariantType, and implements the GetInstance
method to return the TComplexData object (in TypInfo.pas) that is stored in a complex-valued variant's TVarData record.
Delphi:
function TComplexVariantType.GetInstance(const V: TVarData): TObject;
begin
Result := TComplexVarData(V).VComplex;
end;
TPublishableVariantType does the rest. It overrides the GetProperty
and SetProperty
methods to use the runtime type information (RTTI) of the TComplexData object for getting and setting property values.
- Note: For TPublishableVariantType to work, the object that holds the custom variant's data must be compiled with RTTI. This means it must be compiled using the {$M+} compiler directive, or descend from TPersistent.