Шаг 60.
Операции над операндами вариантного типа. Бинарные (двухоперандные) операции

    На этом шаге мы рассмотрим правила выполнения бинарных операций над значениями вариантного типа.

    Если один из операндов бинарной операции имеет тип Variant, второй операнд автоматически преобразуется к вариантному типу, результатом выполнения арифметико-логических операций будет значение вариантного типа, а результатом операций отношения - значение тпа Boolean. Исключение составляют случаи, когда среди операндов есть операнд со значением Unassigned или Null. В первом случае (значение Unassigned) для операндов допустимы только операции отношения, а во втором случае (значение Null) результатом операции всегда будет Null.

    Бинарные операции над операндами типа Variant выполняются в три этапа.

  1. Определяется общий для двух операндов тип.
  2. В зависимости от общего типа и конкретной операции выполняются преобразования типов операндов. Причем, совсем не обязательно к общему типу!
  3. Операция выполняется для каждой пары вариантых операндов различных типов по-своему соответственно описанным ниже правилам.

    Приведем таблицу определения общего типа по типам первого и второго операндов.

Таблица 1. Определение типа результата
  Тип значения второго вариантного операнда
Integer Double Currency String Boolean Date
Тип
значения
первого
вариантного
операнда
Integer Integer Double Currency Double Integer Date
Double Double Double Currency Double Double Date
Currency Currency Currency Currency Currency Currency Date
String Double Double Currency String Boolean Date
Boolean Integer Double Currency Boolean Boolean Date
Date Date Date Date Date Date Date


    Замечание. В этой таблице введены условные обозначения:


    Приведем пример, демонстрирующий нетривиальные законы преобразования вариантных типов при выполнении арифметико-логических операций. В этом примере формируются шесть таблиц, в которых показаны типы и значения результатов арифметико-логических операций для различных комбинаций основных типов операндов.
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
      .   .   .
    Label24: TLabel;
    Label25: TLabel;
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
      .   .   .
    Edit95: TEdit;
    Edit96: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
var
  Switch:Integer;

function GetCodeName(V : Variant): String;
{ Функция возвращает идентификатор кода вариантного типа}
type
  TypeCodeNames = array [$0000..$000D] of String;
const
  CodeNames: TypeCodeNames =
       ('varEmpty','varNull','varSmallint','varInteger', 'varSingle','varDouble',
        'varCurrency','varDate', 'varOleStr','varDispatch','varError','varBoolean',
        'varVariant','varUnknown');
var
  Code : Integer;
begin
  Code := VarType(V) and varTypeMask;
  case Code of
    $0000..$000D: Result:= CodeNames[Code];
           $0011: Result:='varByte';
           $0100: Result:='varString';
           $0FFF: Result:='varTypeMask';
           $2000: Result:='varArray';
    else Result :='varError';
  end;
end;

procedure PrintOperand (Op: Variant; Ed1,Ed2:TEdit);
{ Процедура печатает идентификатор кода операнда }
{ вариантного типа и значение этого операнда в заданных}
{ полях Ed1 и Ed2 }
var
  CodeName, S : String;
begin
  CodeName := GetCodeName(Op);
  Ed1.Text := CodeName;
  if CodeName <> 'varString' then Str(Op:10:4, S)
  else S := Op;
  Ed2.Text := S;
end;

procedure MyAdd (Opl,Op2: Variant; Ed1,Ed2:TEdit);
{ Процедура вьполняет операцию сложения над заданными }
{ операндами вариантного типа Opl и 0р2, а также печатает }
{ идентификатор кода операнда вариантного типа и значение }
{ этого операнда в полях Edl й Ed2}
var
  Res : Variant;
  S   : String;
begin
  try
    Res := Null;
    Res := Opl + Op2;
    Ed1.Text := GetCodeName (Res);
    Str (Res:10:4, S);
    Ed2.Text := S;
  except
    on  EVariantError  do
    begin
      Ed1.Text := 'Error';
      Ed2.Text := 'Error';
    end;
  end;
end;

procedure MySubstr (Opl,Op2: Variant; Ed1,Ed2:TEdit);
{ Процедура вьполняет операцию вычитания над заданными    }
{ операндами вариантного типа Opl и 0р2, а также печатает }
{ идентификатор кода операнда вариантного типа и значение }
{ этого операнда в полях Edl й Ed2}
var
  Res : Variant;
  S   : String;
begin
  try
    Res := Null;
    Res := Opl - Op2;
    Ed1.Text := GetCodeName (Res);
    Str (Res:10:4, S);
    Ed2.Text := S;
  except
    on  EVariantError  do
    begin
      Ed1.Text := 'Error';
      Ed2.Text := 'Error';
    end;
  end;
end;

procedure MyMult (Opl,Op2: Variant; Ed1,Ed2:TEdit);
{ Процедура вьполняет операцию умножения над заданными    }
{ операндами вариантного типа Opl и 0р2, а также печатает }
{ идентификатор кода операнда вариантного типа и значение }
{ этого операнда в полях Edl й Ed2}
var
  Res : Variant;
  S   : String;
begin
  try
    Res := Null;
    Res := Opl * Op2;
    Ed1.Text := GetCodeName (Res);
    Str (Res:10:4, S);
    Ed2.Text := S;
  except
    on  EVariantError  do
    begin
      Ed1.Text := 'Error';
      Ed2.Text := 'Error';
    end;
  end;
end;

procedure MyDivide (Opl,Op2: Variant; Ed1,Ed2:TEdit);
{ Процедура вьполняет операцию деления над заданными      }
{ операндами вариантного типа Opl и 0р2, а также печатает }
{ идентификатор кода операнда вариантного типа и значение }
{ этого операнда в полях Edl й Ed2}
var
  Res : Variant;
  S   : String;
begin
  try
    Res := Null;
    Res := Opl / Op2;
    Ed1.Text := GetCodeName (Res);
    Str (Res:10:4, S);
    Ed2.Text := S;
  except
    on  EVariantError  do
    begin
      Ed1.Text := 'Error';
      Ed2.Text := 'Error';
    end;
  end;
end;

procedure MyShl (Opl,Op2: Variant; Ed1,Ed2:TEdit);
{ Процедура вьполняет операцию сдвига влево над заданными }
{ операндами вариантного типа Opl и 0р2, а также печатает }
{ идентификатор кода операнда вариантного типа и значение }
{ этого операнда в полях Edl й Ed2}
var
  Res : Variant;
  S   : String;
begin
  try
    Res := Null;
    Res := Opl shl Op2;
    Ed1.Text := GetCodeName (Res);
    Str (Res:10:4, S);
    Ed2.Text := S;
  except
    on  EVariantError  do
    begin
      Ed1.Text := 'Error';
      Ed2.Text := 'Error';
    end;
  end;
end;

procedure MyAnd (Opl,Op2: Variant; Ed1,Ed2:TEdit);
{ Процедура вьполняет операцию поразрядного умножения над заданными }
{ операндами вариантного типа Opl и 0р2, а также печатает           }
{ идентификатор кода операнда вариантного типа и значение           }
{ этого операнда в полях Edl й Ed2}
var
  Res : Variant;
  S   : String;
begin
  try
    Res := Null;
    Res := Opl and Op2;
    Ed1.Text := GetCodeName (Res);
    Str (Res:10:4, S);
    Ed2.Text := S;
  except
    on  EVariantError  do
    begin
      Ed1.Text := 'Error';
      Ed2.Text := 'Error';
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Op1, Op2 : Variant;
  Integer1,Integer2 : Integer;
  Double1, Double2 : Double;
  Currency1, Currency2 : Currency;
  TDateTime1, TDateTime2 : TDateTime;
procedure FirstColumn (Op1, Op2 : Variant);
{ Процедура вычисляет значения для формирования первой }
{ колонки таблицы и заносит их в соответствующие поля  }
{ редактирования }
begin
  PrintOperand (Op1, Edit1, Edit2);
  PrintOperand (Op2, Edit3, Edit4);
  MyAdd    (Op1, Op2, Edit5, Edit6);
  MySubstr (Op1, Op2, Edit7, Edit8);
  MyMult   (Op1, Op2, Edit9, Edit10);
  MyDivide (Op1, Op2, Edit11, Edit12);
  MyShl    (Op1, Op2, Edit13, Edit14);
  MyAnd    (Op1, Op2, Edit15, Edit16);
end;
procedure SecondColumn (Op1, Op2 : Variant);
{ Процедура вычисляет значения для формирования второй }
{ колонки таблицы и заносит их в соответствующие поля  }
{ редактирования }
begin
  PrintOperand (Op1, Edit17, Edit18);
  PrintOperand (Op2, Edit19, Edit20);
  MyAdd    (Op1, Op2, Edit21, Edit22);
  MySubstr (Op1, Op2, Edit23, Edit24);
  MyMult   (Op1, Op2, Edit25, Edit26);
  MyDivide (Op1, Op2, Edit27, Edit28);
  MyShl    (Op1, Op2, Edit29, Edit30);
  MyAnd    (Op1, Op2, Edit31, Edit32);
end;
procedure ThirdColumn (Op1, Op2 : Variant);
{ Процедура вычисляет значения для формирования третьей }
{ колонки таблицы и заносит их в соответствующие поля  }
{ редактирования }
begin
  PrintOperand (Op1, Edit33, Edit34);
  PrintOperand (Op2, Edit35, Edit36);
  MyAdd    (Op1, Op2, Edit37, Edit38);
  MySubstr (Op1, Op2, Edit39, Edit40);
  MyMult   (Op1, Op2, Edit41, Edit42);
  MyDivide (Op1, Op2, Edit43, Edit44);
  MyShl    (Op1, Op2, Edit45, Edit46);
  MyAnd    (Op1, Op2, Edit47, Edit48);
end;
procedure FourthColumn (Op1, Op2 : Variant);
{ Процедура вычисляет значения для формирования четвертой }
{ колонки таблицы и заносит их в соответствующие поля  }
{ редактирования }
begin
  PrintOperand (Op1, Edit49, Edit50);
  PrintOperand (Op2, Edit51, Edit52);
  MyAdd    (Op1, Op2, Edit53, Edit54);
  MySubstr (Op1, Op2, Edit55, Edit56);
  MyMult   (Op1, Op2, Edit57, Edit58);
  MyDivide (Op1, Op2, Edit59, Edit60);
  MyShl    (Op1, Op2, Edit61, Edit62);
  MyAnd    (Op1, Op2, Edit63, Edit64);
end;
procedure FifthColumn (Op1, Op2 : Variant);
{ Процедура вычисляет значения для формирования пятой }
{ колонки таблицы и заносит их в соответствующие поля  }
{ редактирования }
begin
  PrintOperand (Op1, Edit65, Edit66);
  PrintOperand (Op2, Edit67, Edit68);
  MyAdd    (Op1, Op2, Edit69, Edit70);
  MySubstr (Op1, Op2, Edit71, Edit72);
  MyMult   (Op1, Op2, Edit73, Edit74);
  MyDivide (Op1, Op2, Edit75, Edit76);
  MyShl    (Op1, Op2, Edit77, Edit78);
  MyAnd    (Op1, Op2, Edit79, Edit80);
end;
procedure SixthColumn (Op1, Op2 : Variant);
{ Процедура вычисляет значения для формирования шестой }
{ колонки таблицы и заносит их в соответствующие поля  }
{ редактирования }
begin
  PrintOperand (Op1, Edit81, Edit82);
  PrintOperand (Op2, Edit83, Edit84);
  MyAdd    (Op1, Op2, Edit85, Edit86);
  MySubstr (Op1, Op2, Edit87, Edit88);
  MyMult   (Op1, Op2, Edit89, Edit90);
  MyDivide (Op1, Op2, Edit91, Edit92);
  MyShl    (Op1, Op2, Edit93, Edit94);
  MyAnd    (Op1, Op2, Edit95, Edit96);
end;
begin {TForm1.Button1}
  case Switch of
    0: begin
         Form1.Caption := 'Таблица преобразования вариантных ' +
                           ' типов для varInteger';
         Integer1 := 4;   Integer2 := 2;
         Op1 := Integer1; Op2 := Integer2; FirstColumn(Op1,Op2);
         Integer1 := 4;   Double2 := 2;
         Op1 := Integer1; Op2 := Double2; SecondColumn(Op1,Op2);
         Integer1 := 4;   Currency2 := 2;
         Op1 := Integer1; Op2 := Currency2; ThirdColumn(Op1,Op2);
         Integer1 := 4;
         Op1 := Integer1; Op2 := '2'; FourthColumn(Op1,Op2);
         Integer1 := 4;
         Op1 := Integer1; Op2 := True; FifthColumn(Op1,Op2);
         Integer1 := 4;   TDateTime2 := 35431;
         Op1 := Integer1; Op2 := TDateTime2; SixthColumn(Op1,Op2);
         Inc(Switch);
       end;
    1: begin
         Form1.Caption := 'Таблица преобразования вариантных ' +
                           ' типов для varDouble';
         Double1 := 4;   Integer2 := 2;
         Op1 := Double1; Op2 := Integer2; FirstColumn(Op1,Op2);
         Double1 := 4;   Double2 := 2;
         Op1 := Double1; Op2 := Double2; SecondColumn(Op1,Op2);
         Double1 := 4;   Currency2 := 2;
         Op1 := Double1; Op2 := Currency2; ThirdColumn(Op1,Op2);
         Double1 := 4;
         Op1 := Double1; Op2 := '2'; FourthColumn(Op1,Op2);
         Double1 := 4;
         Op1 := Double1; Op2 := True; FifthColumn(Op1,Op2);
         Double1 := 4;   TDateTime2 := 35431;
         Op1 := Double1; Op2 := TDateTime2; SixthColumn(Op1,Op2);
         Inc(Switch);
       end;
    2: begin
         Form1.Caption := 'Таблица преобразования вариантных ' +
                           ' типов для varCurrency';
         Currency1 := 4;   Integer2 := 2;
         Op1 := Currency1; Op2 := Integer2; FirstColumn(Op1,Op2);
         Currency1 := 4;   Double2 := 2;
         Op1 := Currency1; Op2 := Double2; SecondColumn(Op1,Op2);
         Currency1 := 4;   Currency2 := 2;
         Op1 := Currency1; Op2 := Currency2; ThirdColumn(Op1,Op2);
         Currency1 := 4;
         Op1 := Currency1; Op2 := '2'; FourthColumn(Op1,Op2);
         Currency1 := 4;
         Op1 := Currency1; Op2 := True; FifthColumn(Op1,Op2);
         Currency1 := 4;   TDateTime2 := 35431;
         Op1 := Currency1; Op2 := TDateTime2; SixthColumn(Op1,Op2);
         Inc(Switch);
       end;
    3: begin
         Form1.Caption := 'Таблица преобразования вариантных ' +
                           ' типов для varString';
         Integer2 := 2;
         Op1 := '4'; Op2 := Integer2; FirstColumn(Op1,Op2);
         Double2 := 2;
         Op1 := '4'; Op2 := Double2; SecondColumn(Op1,Op2);
         Currency2 := 2;
         Op1 := '4'; Op2 := Currency2; ThirdColumn(Op1,Op2);
         Op1 := '4'; Op2 := '2'; FourthColumn(Op1,Op2);
         Op1 := '4'; Op2 := True; FifthColumn(Op1,Op2);
         TDateTime2 := 35431;
         Op1 := '4'; Op2 := TDateTime2; SixthColumn(Op1,Op2);
         Inc(Switch);
       end;
    4: begin
         Form1.Caption := 'Таблица преобразования вариантных ' +
                           ' типов для varBoolean';
         Integer2 := 2;
         Op1 := True; Op2 := Integer2; FirstColumn(Op1,Op2);
         Double2 := 2;
         Op1 := True; Op2 := Double2; SecondColumn(Op1,Op2);
         Currency2 := 2;
         Op1 := True; Op2 := Currency2; ThirdColumn(Op1,Op2);
         Op1 := True; Op2 := '2'; FourthColumn(Op1,Op2);
         Op1 := True; Op2 := True; FifthColumn(Op1,Op2);
         TDateTime2 := 35431;
         Op1 := True; Op2 := TDateTime2; SixthColumn(Op1,Op2);
         Inc(Switch);
       end;
    5: begin
         Form1.Caption := 'Таблица преобразования вариантных ' +
                           ' типов для varDate';
         TDateTime1 := 35431;   Integer2 := 2;
         Op1 := TDateTime1; Op2 := Integer2; FirstColumn(Op1,Op2);
         TDateTime1 := 35431;   Double2 := 2;
         Op1 := TDateTime1; Op2 := Double2; SecondColumn(Op1,Op2);
         TDateTime1 := 35431;   Currency2 := 2;
         Op1 := TDateTime1; Op2 := Currency2; ThirdColumn(Op1,Op2);
         TDateTime1 := 35431;
         Op1 := TDateTime1; Op2 := '2'; FourthColumn(Op1,Op2);
         TDateTime1 := 35431;
         Op1 := TDateTime1; Op2 := True; FifthColumn(Op1,Op2);
         TDateTime1 := 35431;   TDateTime2 := 35431;
         Op1 := TDateTime1; Op2 := TDateTime2; SixthColumn(Op1,Op2);
         Switch:=0;
       end;
  end; {Case}
end; {TForm1.Button1}

procedure TForm1.FormCreate(Sender: TObject);
begin
  Form1.Caption := 'Демонстрация преобразования '+
                    ' вариантных типов';
end;

initialization
      Switch:=0; 
end.
Текст этого примера можно взять здесь.

    В результате выполнения проекта, использующего приведенный модуль, при нажатии на кнопку будут поочередно появляться на экране шесть таблиц, одна из которых изображена на рисунке 1:


Рис.1. Пример работы приложения

    Операции отношения (=, <>, <, >, <=, >=) выполняются в следующем порядке. Сначала оба операнда преобразуются к общему типу, а затем над полученными значениями выполняется операция, результатом которой будет значение булевского типа.

    Стандартные значения вариантных переменных Unassigned и Null состоят в таком отношении к прочим возможным значениям:

    Unassigned < Null  < ЛюбоеДругоеЗначение 

    На следующем шаге мы рассмотрим выполнение унарных операций над значениями вариантного типа.




Предыдущий шаг Содержание Следующий шаг