【问题标题】:Delphi Form with custom constructor as the mainform?以自定义构造函数为主要形式的Delphi Form?
【发布时间】:2014-11-25 18:55:29
【问题描述】:

我想要一个从具有自定义构造函数的 BaseForm 派生的 MainForm。由于这是 Mainform,它是通过调用 *.dpr 文件中的 Application.CreateForm(TMyMainForm, MyMainForm) 创建的。但是,在表单创建期间不会调用我的自定义构造函数。

显然,如果我调用 MyMainForm := TMyMainForm.Create(AOwner),它可以正常工作。我可以不使用带有自定义构造函数的表单作为主表单吗?

TBaseForm = class(TForm)
  constructor Create(AOwner:TComponent; AName:string);reintroduce;
end;

TMyMainForm = class(TBaseForm)
  constructor Create(AOwner:TComponent);reintroduce;
end;  

constructor TBaseForm.Create(AOwner:TComponent);

begin;
  inherited Create(AOwner);
end;

constructor TMyMainForm.Create(AOwner:TComponent);

begin;
  inherited Create(AOwner, 'Custom Constructor Parameter');
end;  

【问题讨论】:

    标签: delphi constructor tform


    【解决方案1】:

    Application.CreateForm() 不能调用 reintroduce 的构造函数。创建新对象时,它调用TComponent.Create() 构造函数,并期望多态性调用任何派生构造函数。通过reintroduce'ing 您的自定义构造函数,您不是多态调用链的一部分。 reintroduce 公开了一个全新的方法,它与继承的方法具有相同的名称,但与之无关。

    要执行您正在尝试的操作,请不要将reintroduce 用于您的构造函数。在您的 Base 表单中创建一个单独的构造函数,然后将 MainForm override 作为标准构造函数并调用您的自定义 Base 构造函数,例如:

    TBaseForm = class(TForm)
      constructor CreateWithName(AOwner: TComponent; AName: string); // <-- no reintroduce needed since it is a new name
    end;
    
    TMyMainForm = class(TBaseForm)
      constructor Create(AOwner: TComponent); override; // <-- not reintroduce
    end;  
    
    constructor TBaseForm.CreateWithName(AOwner: TComponent; AName: string);
    begin;
      inherited Create(AOwner);
      // use AName as needed...
    end;
    
    constructor TMyMainForm.Create(AOwner: TComponent);
    begin;
      inherited CreateWithName(AOwner, 'Custom Constructor Parameter');
    end;  
    

    【讨论】:

    • 大多数时候,所有人都在尝试通过这种方式做的就是通过构造函数来初始化派生类中的成员。我不推荐这个。相反,您还可以定义公共属性并在创建派生表单后对其进行初始化。这是一两行额外的代码,但那又怎样?
    • 带参数的构造函数的优点是你必须传递一些东西,所以你不能忘记传递所需的设置。可以忘记设置属性。这并不是因为人们想要节省输入几行代码,而仅仅是因为它是更好的策略,IMO。
    • 这个计划的原因正是 Rudy 指出的,@RudyVelthuis 我的目的是有一个具有背景、处理 NCHitTest、记住最后位置、大小等的通用基本形式(从注册表写入/读取) 并从中派生出我所有未来的项目。有没有比上面的策略更好的方法?
    • @ss2006 为什么需要构造函数参数?你会通过什么?
    • @DavidHeffernan - 项目的描述性名称,BaseForm 将使用它来创建注册表项并说保存表单的位置和大小(以简化从上一个会话),或者根据字符串使用不同的createparams
    【解决方案2】:

    为了使表单成为 VCL 主表单,必须通过调用 Application.CreateForm 来创建它。这反过来调用TComponent 中声明的虚拟构造函数。因此,没有办法为 VCL 主窗体调用不同的构造函数。

    Remy 提出了一个选项。覆盖TComponent 中声明的构造函数,并调用不同的构造函数传递额外的参数。这可以是同一个类中的构造函数,也可以是从基类继承的构造函数。

    这里的另一个选择是在基类上使用抽象类方法。例如,

    type
      TMainFormBase = class(TForm)
      protected
        class function ProjectName: string; virtual; abstract;
        class function RegKeyPath: string; virtual; abstract;
      end;
    

    在您的派生类中,您将覆盖这些抽象方法。基础表单类中的代码可以调用这些方法,而不是在构造函数中设置属性。当然,如果你需要在构造函数中工作,你完全可以从构造函数中调用这些方法。

    我个人对向已经有虚拟构造函数的类添加新构造函数有偏见。虚拟构造函数范式引导您使用单个虚拟构造函数,仅此而已。如果您开始在层次结构的下方添加不同的构造函数,那么很容易因虚拟实例化时调用错误的构造函数而导致麻烦。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-21
      • 2013-04-26
      • 2017-07-07
      • 2020-04-23
      • 1970-01-01
      相关资源
      最近更新 更多