我一直在尝试通过覆盖在该基类中定义的虚方法来扩展从同一基类继承的一堆库类。修改总是相同的,所以我决定创建一个由库类类参数化的泛型类,而不是创建库类的N个后继类,它继承自参数指定的类,并覆盖基类的方法。 问题是下面的代码没有编译,编译器不允许继承自T:
program Project1;
type
LibraryBaseClass = class
procedure foo; virtual;
end;
LibraryClassA = class(LibraryBaseClass)
end;
LibraryClassB = class(LibraryBaseClass)
end;
LibraryClassC = class(LibraryBaseClass)
end;
LibraryClassD = class(LibraryBaseClass)
end;
MyClass<T:LibraryBaseClass> = class(T) //Project1.dpr(20) Error: E2021 Class type required
procedure foo; override;
end;
procedure LibraryBaseClass.foo;
begin
end;
procedure MyClass<T>.foo;
begin
end;
begin
MyClass<LibraryClassA>.Create.foo;
MyClass<LibraryClassB>.Create.foo;
MyClass<LibraryClassC>.Create.foo;
MyClass<LibraryClassD>.Create.foo;
end.
任何想法如何使这项工作?也许有一种方法可以欺骗编译器接受等价的东西,例如,继承Dictionary<T,T>
编译没有问题。
如果你和我有同样的目标,你会怎么做?请记住,在实际情况中,我需要覆盖多个方法并添加一些数据成员。
谢谢
答案 0 :(得分:9)
正如您已经被告知的那样,这对C ++模板有效,而不适用于C#或Delphi泛型。模板和泛型之间的根本区别在于,从概念上讲,每个模板实例化都是完全独立编译的类型。对于所有可能的类型,泛型编译一次。从类型参数派生时,这是不可能的,因为你可以获得诸如
之类的结构type
LibraryBaseClass = class
procedure foo; virtual;
end;
LibraryClassA = class(LibraryBaseClass)
procedure foo; reintroduce; virtual;
end;
LibraryClassB = class(LibraryBaseClass)
end;
MyClass<T:LibraryBaseClass> = class(T)
procedure foo; override; // overrides LibraryClass.foo or LibraryClassA.foo ?
end;
然而,这可以在C ++中运行,因为在C ++中MyClass<LibraryClassA>
和MyClass<LibraryClassB>
完全分开,并且在实例化MyClass<LibraryClassA>
时,foo
被查找并在{{LibraryClassA
中找到1}}在找到基类方法之前。
如果你和我有同样的目标,你会怎么做?请记住,在实际情况中,我需要覆盖多个方法并添加一些数据成员。
可能在运行时创建类型,但几乎可以肯定是极坏主意。我不得不一次使用它,并希望避免它。它涉及读取VMT,创建它的副本,在某处存储原始LibraryBaseClass.foo
方法指针的副本,修改VMT以指向自定义方法,并从该重写函数调用原始存储的方法指针。当然没有内置语言支持,并且无法从代码中引用派生类型。
我曾经在C#中有过这样的需求,但是在那种情况下我很幸运,只有四种可能的基类。我最终手动创建了四个单独的派生类,实现了四次方法,并使用查找结构(Dictionary<,>
)将正确的基类映射到正确的派生类。
请注意,是针对特定情况的技巧,不适用于您的问题,但可能会帮助其他读者:如果您的派生类必须都实现相同的接口,并且不需要新的数据成员或函数覆盖,您可以避免多次编写实现:
type
IMySpecialInterface = interface
procedure ShowName;
end;
TMySpecialInterfaceHelper = class helper for TComponent
procedure ShowName;
end;
procedure TMySpecialInterfaceHelper.ShowName;
begin
ShowMessage(Name);
end;
type
TLabelWithShowName = class(TLabel, IMySpecialInterface);
TButtonWithShowName = class(TButton, IMySpecialInterface);
在这种情况下,类助手方法实现将是接口方法的有效实现。
答案 1 :(得分:4)
在Delphi XE及更高版本中,您还可以尝试完全不同的东西:TVirtualMethodInterceptor
。
答案 2 :(得分:3)
使用Delphi泛型无法实现您的尝试。
对于它的价值,等效代码在C#泛型中也是无效的。但是,您的设计适用于C ++模板。
答案 3 :(得分:0)
我可能误解了你对这个问题的描述,但是从你的简化例子来看,似乎你可以“扭转它”并在中间的层次结构中插入一个类,如下所示:
program Project1;
type
LibraryBaseClass = class
procedure foo; virtual;
end;
LibraryBaseFooClass = class(LibraryBaseClass)
procedure foo; override;
end;
LibraryClassA = class(LibraryBaseFooClass)
end;
LibraryClassB = class(LibraryBaseFooClass)
end;
LibraryClassC = class(LibraryBaseFooClass)
end;
LibraryClassD = class(LibraryBaseFooClass)
end;
procedure LibraryBaseClass.foo;
begin
end;
procedure LibraryBaseFooClass.foo;
begin
end;
begin
LibraryClassA.Create.foo;
LibraryClassB.Create.foo;
LibraryClassC.Create.foo;
LibraryClassD.Create.foo;
end.