この場合、DoSomething プロシージャはクラス階層の外に存在する必要があります。構造を逆にすることはできないので、クラス継承とポリモーフィズムを利用できません。さらに、これはほんの一例です。ここでは、実行時にオブジェクトをその親クラスにキャストするにはどうすればよいかという中心的な質問に焦点を当てたいと思います。
これを理解する鍵は、Delphi が静的に型付けされた言語であるという事実です。また、非ポリモーフィック プロシージャを呼び出していることにも注意してください。つまり、パラメーターの型はコンパイル時に決定されます。そして、オーバーロードの解決はその型に基づいています。したがって、オーバーロードの解決はコンパイル時に行われます。
だから、あなたの例:
DoSomething(TClass1(AObj))
コンパイル時にパラメーターの型がわかっているため、必要なことを行います。次のようなものを作ることは単に不可能です
DoSomething(AObj.ClassParent(AObj))
コンパイル時にパラメーターの型を知る必要があるため、必要なことを行います。
実行時にオブジェクトをその親クラスにキャストするにはどうすればよいですか?
これが問題の核心です。キャストは実行時の構造ではなく、コンパイル時の構造です。したがって、簡単な答えは、オブジェクトをそのランタイム型にキャストできないということです。
ポリモーフィック ディスパッチを使用できない場合は、ハードコーディングされたキャストしか選択肢がありません。Cosmin's answer の例は、非常に使いやすい方法でそれを行う方法を示していますが、コンパイル時にオーバーロードが解決されるという事実は残っています。それを逃れる方法はまったくありません。
ここで 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
明らかに、このようなアプローチは、命名規則に従うことに依存しています。
ハードコーディングされたキャスト バリアントに対するこのアプローチの主な利点の 1 つは、継承されたメソッドを呼び出す順序がクラス階層によって決定されることです。