RAD Studio
ContentsIndex
PreviousUpNext
Supporting Properties and Methods in Custom Variants

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.

To provide support for properties and methods, the class you create to enable the new custom variant type should descend from TInvokeableVariantType instead of directly from TCustomVariantType

TInvokeableVariantType defines four methods:

  • DoFunction
  • DoProcedure
  • GetProperty
  • SetProperty
that you can implement to support properties and methods on your custom variant type. 

For example, the 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:

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 runtime (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.

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 variant's data. If you use 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 variant's data 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:

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.

Copyright(C) 2009 Embarcadero Technologies, Inc. All Rights Reserved.
What do you think about this topic? Send feedback!