具有类功能的类型转换

时间:2012-05-13 14:05:27

标签: delphi casting

我想要使用类函数进行类型转换。 我有基(TBase),派生(TDer)和类型转换(TMyType)类。

Ver:Delphi 7

TBase = class;
TDer = class;
TMyType = class;

TBase = class
  function Say : String;
  class function MYType:TMyType;
end;

TDer = class(TBase)
  a: string;
  b: string;
  function Say2 : String;
end;

TMyType=class(TBase)
  class function AsDer:TDer;
end;

{ TBase }

class function TBase.MYType: TMyType;
begin
  Result:=TMyType(Self);
end;

function TBase.Say: String;
begin
   Result:='TBase';
end;

{ TDer }

function TDer.Say2: String;
begin
  Result:='TDer';
end;

{ TMyType }

class function TMyType.AsDer: TDer;
begin
  Assert(Assigned(Self));
  Result := TDer(Self) ;
end;

示例用法如下,它是调用方法,但是当设置/获取字段的引发错误时。

procedure TForm1.Button1Click(Sender: TObject);
var
  b,c:TBase;
begin
  b:=TDer.Create;
  c:=b.MYType.AsDer;

  ShowMessage(b.MYType.AsDer.Say2); // OK. Running      
  if (@b<>@c) then ShowMessage('Not Equal');  // Shows message, Why ?
  b.MYType.AsDer.a:='hey'; // Error

  FreeAndNil(b);
end;

你有什么想法吗?

2 个答案:

答案 0 :(得分:6)

根本问题在于:

class function TBase.MYType: TMyType;
begin
  Result:=TMyType(Self);
end;

这是一种类方法,因此Self指的是,而不是实例。将它作为一个实例进行转换并不是这样。您的AsDer类函数中出现了完全相同的错误。

查看细节,调用

b.MYType.AsDer.Say2

是良性的,似乎工作正常,因为它没有引用Self。您可以同样编写TDer(nil).Say2并且该代码也可以正常工作。现在,如果函数Say2引用Self,即引用实例,则会出现运行时错误。

@b<>@c

总是计算为true,因为您正在比较两个不同局部变量的位置。

b.MYType.AsDer.a

是运行时错误,因为AsDer未返回TDer的实例。因此,当您尝试写入a时,您会遇到运行时错误。这是因为您指的是Self,这就是为什么此代码失败,但之前对Say2的调用没有。


我不确定你在这里要做什么,但看起来都错了。即使您使用的是实例方法而不是类方法,将基类实例用于派生类实例也是错误的。如果某些东西是错误的类型,那么任何数量的铸造都不会将它变成正确的类型。

此外,您永远不应该编写方法为TBase的代码,假设它是TDerived类型。基类应该完全不知道它的派生类。这是OOP设计的基本原则之一。

答案 1 :(得分:0)

以下是编辑的新版本:

TBase = class;
TDer = class;
TMyType = class;

TBase = class
  MYType:TMyType;
  constructor Create;
  destructor Destroy;
  function Say : String;
end;

TDer = class(TBase)
  a: string;
  b: string;
  function Say2 : String;
end;

TMyType=class
public
  T: TObject;
  function AsDer:TDer;
end;

{ TBase }

constructor TBase.Create;
begin
  MYType:=TMYType.Create;
  MYType.T:=TObject(Self);
end;

destructor TBase.Destroy;
begin
  MYType.Free;
end;

function TBase.Say: String;
begin
  Result:='TBase';
end;

{ TDer }

function TDer.Say2: String;
begin
  Result:='TDer';
end;

{ TMyType }

function TMyType.AsDer: TDer; 
begin
  Result := TDer(T) ;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  b:TBase;
  c:TDer;
begin
  b:=TDer.Create;
  TDer(b).a:='a';
  c:=b.MYType.AsDer;

  ShowMessage('b.MYType.AsDer='+b.MYType.AsDer.a+', c.a ='+ c.a); // OK. Running
  FreeAndNil(b);
end;