我有一个TChildClass
派生的子类TBaseClass
。 TBaseClass
具有function foo: string;
必须始终实现的方法TChildClass
!
IMyInterface = Interface(IInterface)
function foo: string;
end;
TBaseClass = class(TInterfacedObject, IMyInterface)
public
function foo: string;
end;
TChildClass = class(TBaseClass , IMyInterface)
public
function foo: string;
end;
我希望TChildClass
始终实现function foo
并调用从TBaseClass
继承的呼叫:
function TBaseClass.foo: string
begin
Result := 'Hello';
end;
function TChildClass.foo: string
begin
Result := inherited;
Result := Result + ' world!';
end;
如何制作?
答案 0 :(得分:6)
您不能强制在编译时要求覆盖。
为了使TChildClass
覆盖foo()
,需要在foo()
中将virtual
声明为TBaseClass
(但不能abstract
声明为{因为您希望TBaseClass.foo()
具有默认实现,否则编译器会抱怨!)。而且,与C ++不同,Delphi不会要求被覆盖的抽象方法,并且它允许代码在运行时创建抽象类的实例(即使调用具有未被覆盖会导致运行时错误。
但是,您可以在运行时验证,TBaseClass.foo()
是否已在子孙中被覆盖,例如:
type
IMyInterface = Interface(IInterface)
function foo: string;
end;
TBaseClass = class(TInterfacedObject, IMyInterface)
public
function foo: string; virtual;
end;
TChildClass = class(TBaseClass, IMyInterface)
public
function foo: string; override;
end;
function TBaseClass.foo: string;
type
TFoo = function: string of object;
var
Impl, Base: TFoo;
ClassTBase: TClass;
begin
Impl := foo;
ClassTBase := TBaseClass;
Base := TBaseClass(@ClassTBase).foo;
if TMethod(Impl).Code = TMethod(Base).Code then
raise Exception.CreateFmt('foo() not implemented in class ''%s''', [ClassName]);
Result := 'Hello';
end;
function TChildClass.foo: string;
begin
Result := inherited foo;
Result := Result + ' world!';
end;
但是,您无法执行强制 TChildClass.foo()
呼叫inherited
,这完全取决于TChildClass
自行决定。< / p>
答案 1 :(得分:2)
您本身不能。但是,您可以通过稍微不同的方式实现您想要的。
type
IMyInterface = Interface(IInterface)
function foo: string;
end;
TBaseClass = class(TInterfacedObject, IMyInterface)
protected
function fooForced : string; virtual; abstract;
public
function foo: string;
end;
TChildClass = class(TBaseClass , IMyInterface)
protected
function fooForced: string; override;
end;
function TBaseClass.foo: string;
begin
Result := 'Hello' + FooForced;
end;
function TChildClass.fooForced: string;
begin
Result := ' world!';
end;
注意-您必须注意“正在创建抽象类”警告!
答案 2 :(得分:2)
我认为以一种或另一种方式使用abstract
方法可能是最干净的方法。就像已经提到的Dsm一样,您无法完全获得 ,因为抽象方法没有实现,因此您不能在基类中调用该方法。
这个替代方案非常接近,但是它需要深入研究RTTI。对于您而言,是否值得取决于您。基本上可以做什么:深入研究类的方法列表。对于具有给定名称的方法,请检查其实现者的类名是什么。如果是TBaseClass,则抛出异常,否则继续。
我非常确定,如果您实现方法foo
的重载,他将失败,因此它不是防水的。也许有一些方法可以检查找到的方法是否确实是替代方法,但是TRttiMethod似乎并没有提供任何可用的方法。
该代码示例的灵感来自RRUZ's answer, here。
TBaseClass = class(TInterfacedObject, IMyInterface)
private
procedure ValidateDescendantImplements(MethodName: string);
public
function foo: string;
end;
function TBaseClass.foo: string;
begin
ValidateDescendantImplements('foo');
Result := 'Hello';
end;
procedure TBaseClass.ValidateDescendantImplements(MethodName: string);
var
m: TRttiMethod;
begin
for m in TRttiContext.Create.GetType(Self.ClassType).GetDeclaredMethods do
begin
if SameText(m.Name, MethodName) then
if m.Parent.Name <> TBaseClass.ClassName then
Exit
end;
raise Exception.CreateFmt('%s needs to be overridden', [MethodName]);
end;
用法:
TChildClass.Create.foo; // Works
TBaseClass.Create.foo; // Throws exception