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

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

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

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

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

    Таблица 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

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

    • коды вариантных типов varSmallint, varInteger и varByte обозначен как Integer;
    • коды вариантных типов varSingle и varDouble обозначены как Double;
    • коды вариантных типов varOleStr and varString обозначены как String.

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

    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  < ЛюбоеДругоеЗначение 
    

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



    Вы можете оставить комментарий, или Трекбэк с вашего сайта.

    Оставить комментарий