delphi - Delphi 中将对象强制转换为父类

标签 delphi class parent

给定以下类层次结构:

TClass1 = class
end;

TClass2a = class(TClass1)
end;

TClass2b = class(TClass1)
end;

我使用以下重载程序对它们进行操作

procedure DoSomething(AObj : TClass1); overload;
begin
  // ...Do something for TClass1
end

procedure DoSomething(AObj : TClass2a); overload;
begin
  // Do something as parent class
  DoSomething(TClass1(AObj))

  // ...Do something for TClass2a
end

procedure DoSomething(AObj : TClass2b); overload;
begin
  // Do something as parent class
  DoSomething(TClass1(AObj))

  // ...Do something for TClass2b
end

如何动态地将每个参数转换为其父类,而不是对其进行硬编码?
我想替换这个:

// Do something as parent class
DoSomething(TClass1(AObj))

使用更通用的东西,比如这样

// Do something as parent class
DoSomething(AObj.ClassParent(AObj))



更新:在这种情况下,DoSomething 过程必须驻留在类层次结构之外。我无法反转结构,因此无法利用类继承和多态性。
此外,这只是一个示例。我希望答案集中在核心问题上:如何在运行时将对象转换为其父类

最佳答案

DoSomething procedures must reside outside the class hierarchy in this case. I can't reverse the structure, so I can't take advantage of class inheritance and polymorphism. Furthermore, this is just an example. I'd like to focus on the core question: how can I cast an object to its parent class at runtime.

理解这一点的关键是 Delphi 是一种静态类型语言。还要记住,您正在调用非多态过程。这意味着参数的类型是在编译时确定的。重载决策基于该类型。因此,重载解析发生在编译时。

所以,你的例子:

DoSomething(TClass1(AObj))

执行您想要的操作,因为参数的类型在编译时已知。根本不可能做出类似的东西

DoSomething(AObj.ClassParent(AObj))

做你想做的事,因为参数的类型必须在编译时知道。

How can I cast an object to its parent class at runtime?

这才是问题的关键。强制转换不是运行时构造,而是编译时构造。所以简单的答案是你不能将对象转换为其运行时类型。

如果您无法使用多态调度,那么您唯一的选择是硬编码强制转换。 Cosmin 答案中的示例展示了如何以一种非常可用的方式做到这一点,但事实仍然是,重载是在编译时解决的。根本没有办法逃避这一点。

<小时/>

您在评论中询问 RTTI 是否有帮助。好吧,正如已经讨论过的,它不会帮助您解决任何转换或重载问题。但是,它可以帮助您避免大量样板硬编码转换。这是一个简单的例子:

program Project1;

{$APPTYPE CONSOLE}

uses
  System.TypInfo,System.Rtti;

type
  TClass1 = class
  end;
  TClass1Class = class of TClass1;

  TClass2a = class(TClass1)
  end;

  TClass2b = class(TClass1)
  end;

type
  TClass1Dispatcher = class
  private
    class var Context: TRttiContext;
  public
    class procedure DoSomething_TClass1(AObj: TClass1);
    class procedure DoSomething_TClass2a(AObj: TClass2a);
    class procedure DoSomething_TClass2b(AObj: TClass2b);
    class procedure DoSomething(AObj: TClass1; AClass: TClass1Class); overload;
    class procedure DoSomething(AObj: TClass1); overload;
  end;

class procedure TClass1Dispatcher.DoSomething_TClass1(AObj: TClass1);
begin
  Writeln('DoSomething_TClass1');
end;

class procedure TClass1Dispatcher.DoSomething_TClass2a(AObj: TClass2a);
begin
  Writeln('DoSomething_TClass2a');
end;

class procedure TClass1Dispatcher.DoSomething_TClass2b(AObj: TClass2b);
begin
  Writeln('DoSomething_TClass2b');
end;

class procedure TClass1Dispatcher.DoSomething(AObj: TClass1; AClass: TClass1Class);
var
  LType: TRttiType;
  LMethod: TRttiMethod;
begin
  if AClass<>TClass1 then
    DoSomething(AObj, TClass1Class(AClass.ClassParent));
  LType := Context.GetType(TypeInfo(TClass1Dispatcher));
  LMethod := LType.GetMethod('DoSomething_'+AClass.ClassName);
  LMethod.Invoke(Self, [AObj]);
end;

class procedure TClass1Dispatcher.DoSomething(AObj: TClass1);
begin
  DoSomething(AObj, TClass1Class(AObj.ClassType));
end;

begin
  TClass1Dispatcher.DoSomething(TClass1.Create);
  TClass1Dispatcher.DoSomething(TClass2a.Create);
  TClass1Dispatcher.DoSomething(TClass2b.Create);
  Readln;
end.

输出:

DoSomething_TClass1
DoSomething_TClass1
DoSomething_TClass2a
DoSomething_TClass1
DoSomething_TClass2b

显然,这种方法依赖于您遵循命名约定。

与硬编码转换变体相比,此方法的主要好处之一是调用继承方法的顺序由类层次结构确定。

关于delphi - Delphi 中将对象强制转换为父类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12742965/

相关文章:

MySQL TAdoConnnection 连接 "connected"属性错误设置为 True

java - 在外部类中使用微调器,因为所有其他 Activity 都需要

c++ - C++ 中的函数引用和 assert(0)

javascript - 循环遍历 div 的所有子级

jquery - 将子 div11 显示在与另一个子标签 p 相同的父 div 中

delphi - 为什么我会收到 "EDBClient: Mismatch in datapacket"异常?

Delphi 和 IdFtp - 如何将所有文件上传到目录中

delphi - 为什么不能在 64 位 Delphi 中获取嵌套局部函数的地址?

class - 在 Scala final case 类中使用 new

html - 如何让我的 div 与父容器中的另一个 div 相邻?