在这种情况下,DoSomething 过程必须位于类层次结构之外。我无法反转结构,因此无法利用类继承和多态性。此外,这只是一个例子。我想关注核心问题:如何在运行时将对象强制转换为其父类。
理解这一点的关键是 Delphi 是一种静态类型语言。还要记住,您正在调用非多态过程。这意味着参数的类型是在编译时确定的。重载决议基于该类型。因此,重载决议发生在编译时。
所以,你的例子:
DoSomething(TClass1(AObj))
做你想做的,因为参数的类型在编译时是已知的。根本不可能做出类似的东西
DoSomething(AObj.ClassParent(AObj))
做你想做的,因为参数的类型必须在编译时知道。
如何在运行时将对象强制转换为其父类?
这是问题的症结所在。强制转换不是运行时构造,而是编译时构造。所以简单的答案是您不能将对象强制转换为它的运行时类型。
如果您不能使用多态调度,那么您唯一的选择是硬编码强制转换。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
显然,这种方法依赖于您遵循命名约定。
这种方法相对于硬编码转换变体的主要好处之一是调用继承方法的顺序由类层次结构决定。