оператор перегрузки и тип преобразования - PullRequest
5 голосов
/ 03 марта 2012

У меня проблема с преобразованием типа с использованием перегрузки оператора, здесь у меня есть полный пример кода:

program OVerloads;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

type
  TMyRange = record
  private type
    TMyRangeEnum = 1 .. 90;
  var
    FMyRangeEnum: TMyRangeEnum;
  public
    class operator Implicit(const Value: Integer): TMyRange;
    class operator Implicit(const Value: TMyRange): Integer;
    class operator Explicit(const Value: Integer): TMyRange;
    class operator Explicit(const Value: TMyRange): Integer;
  end;

class operator TMyRange.Implicit(const Value: Integer): TMyRange;
begin
    Result.FMyRangeEnum := Value;
end;

class operator TMyRange.Implicit(const Value: TMyRange): Integer;
begin
  Result := Value.FMyRangeEnum;
end;

class operator TMyRange.Explicit(const Value: Integer): TMyRange;
begin
  Result.FMyRangeEnum := Value;
end;

class operator TMyRange.Explicit(const Value: TMyRange): Integer;
begin
  Result := Value.FMyRangeEnum;
end;

var
 MyValue: TMyRange;
 MyVar: TMyRange; // (or Integer, not change nothing)
 MyArr: array[1..90] of TMyRange; // (or Integer, not change nothing) 
begin
  try
    { TODO -oUser -cConsole Main : Insert code here }

    MyValue := 90;
    Writeln(MyValue);  // [1] Not work, error: E2054 Illegal in Write/Writeln statement

    Writeln(Integer(MyValue));  // Work

    MyVar := MyArr[MyValue];   // Not work, error: E2010 Incompatible types: 'Integer' and 'TMyRange'                

    MyVar := MyArr[Integer(MyValue)];  // work      

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

Возможно, я что-то забыл, но как я могу решить проблему [1] в mnode, которая не требует явного целого все время (целое число)? Еще раз большое спасибо.

Ответы [ 3 ]

3 голосов
/ 03 марта 2012

Вы, кажется, слишком усложняете это.Определите тип диапазона следующим образом:

type
  TMyRange = 1..90;

Затем объявите свой массив следующим образом:

var
  MyArr: array[TMyRange] of TMyRange;

И все.


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

Насколько я могу судить, вы можете положиться на неявное приведение дляфактические параметры и правая часть операторов присваивания.

Вот мой минимальный пример для демонстрации параметров:

program Overloads;
{$APPTYPE CONSOLE}
type
  TRec = record
  private
    function GetOrd: Integer;
  public
    class operator Implicit(const Value: TRec): Integer;
    property ord: Integer read GetOrd;
  end;

class operator TRec.Implicit(const Value: TRec): Integer;
begin
  Result := 0;
end;

function TRec.GetOrd: Integer;
begin
  Result := 0;
end;

procedure Foo(i: Integer);
begin
end;

var
  R: TRec;
  a: array[0..0] of Integer;

begin
  Writeln(R);//E2054 Illegal type in Write/Writeln statement
  Writeln(Integer(R));//explicit cast, provided by class operator Implicit
  Writeln(R.ord);//my preferred option, a property
  a[R] := 0;//E2010 Incompatible types: 'Integer' and 'TRec'
  a[Integer(R)] := 0;//again, explicit cast is fine
  a[R.ord] := 0;//or using a property
  Foo(R);//implicit cast used for actual parameters
end.

Примечания :

  1. Если вы предоставляете неявный оператор приведения, то нет необходимости также указывать явный оператор приведения.Явное приведение приведет к вызову неявного оператора.
  2. Я думаю, что свойство типа записи приводит к более читаемому коду, чем явное приведение.
0 голосов
/ 03 марта 2012

Единственный способ, которым я вижу, - перегрузка функции Writeln:

procedure Writeln(wrt: Variant);
begin
  System.Writeln(wrt);
end;

Вы должны либо изменить свой обработчик исключений, чтобы использовать System.Writeln, либо объединить строки, потому что число открытых параметров не поддерживается для функций Delphi.

Edit:
Как вы упомянули в своем комментарии, вы получаете ошибку при попытке использовать вашу запись в качестве индекса массива.

Это связано с тем, что индексам массивов, как и некоторым другим конструкциям, таким как, например, переменные цикла и переключатели case, требуются порядковые типы (например, Integer, Byte, Word ...).
Передача записи не будет работать, потому что запись не является порядковым типом. Возможность привести его к порядковому типу не делает его единым целым.

Существует простое решение:

Определите TMyRangeEnum как отдельный тип, без записи с приложением.
Это решит не только проблемы, с которыми вы столкнулись, но и проблемы, с которыми вы еще не сталкивались:

MyValue := 91;

приведет к ошибке компилятора вместо установки недопустимого значения для переменной enum.

И так будет:

MyValue := 256;

приведет к такой ошибке вместо переполнения (вместо него будет назначено 0, что также не является допустимым значением в перечислении).

0 голосов
/ 03 марта 2012

Я не верю, что неявные операторы работают в Write / WriteLn, так как они являются магическими функциями компилятора, а не "правильными" функциями, которые принимают определенный тип.

...