过载运算符和转换类型

时间:2012-03-03 13:19:08

标签: delphi delphi-xe2

我有一个关于使用运算符重载转换类型的问题,这里我有一个完整的示例代码:

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.

可能我忘了一些东西,但是因为我可以在mnode中解决问题[1],不需要显式的所有时间整数(myValue)? 再次感谢,非常感谢。

3 个答案:

答案 0 :(得分:3)

你似乎过于复杂了。定义这样的范围类型:

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. 我认为记录类型上的属性导致代码比显式强制转换更易读。

答案 1 :(得分:0)

我不相信隐式运算符在Write / WriteLn中工作,因为它们是编译器魔术函数而不是采用特定类型的“正确”函数。

答案 2 :(得分:0)

我看到的唯一方法是重载Writeln函数:

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

您必须更改异常处理程序以使用System.Writeln或连接字符串,因为Delphi函数不支持打开参数计数。

修改
就像你在评论中提到的那样,在尝试将记录用作数组索引时会出现错误。

这是因为数组索引以及其他一些结构(例如循环变量和case开关)需要序数类型(例如IntegerByte,{{1} } ...) 传递记录在那里不起作用,因为记录不是序数类型。将其转换为序数类型的可能性并不一定。

有一个简单的解决方案:

Word定义为独立类型,没有包含它的记录 这不仅可以解决您遇到的问题,还可以解决您尚未遇到的问题:

TMyRangeEnum

将导致编译器错误,而不是将枚举变量设置为无效值。

同样会:

MyValue := 91;

导致出现这样的错误,而不是溢出(将分配0,这也不是枚举中的有效值。)