4

我一直在尝试通过覆盖该基类中定义的虚拟方法来扩展一堆从同一基类继承的库类。修改总是相同的,所以我决定创建一个由库类类型参数化的泛型类,而不是创建库类的 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>编译器继承而不会出现问题。

或者如果你和我有同样的目标你会怎么做?请记住,在实际情况中,我需要覆盖多个方法并添加一些数据成员。

谢谢

4

4 回答 4

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

或者如果你和我有同样的目标你会怎么做?请记住,在实际情况中,我需要覆盖多个方法并添加一些数据成员。

可以在运行时创建类型,但几乎可以肯定这是一个非常糟糕的主意。我不得不利用它一次,并且很想避免它。它包括读取 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);

在这种情况下,类帮助方法实现将是接口方法的有效实现。

于 2012-09-04T14:12:09.907 回答
4

在 Delphi XE 及更高版本中,您还可以尝试一些完全不同的东西:TVirtualMethodInterceptor.

于 2012-09-04T18:09:09.227 回答
3

Delphi 泛型根本无法实现您尝试做的事情。

对于它的价值,等效代码在 C# 泛型中也是无效的。但是,您的设计可以使用 C++ 模板。

于 2012-09-04T13:59:50.520 回答
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.
于 2012-09-04T14:13:29.640 回答