【问题标题】:Access Violation when handling forms处理表单时的访问冲突
【发布时间】:2014-02-21 01:33:58
【问题描述】:

我有这样的程序在 TForm 上显示/隐藏一个元素:

procedure ShowHideControl(const ParentForm: TForm; const ControlName: String; ShowControl: Boolean);
var
  i: Integer;
begin
  for i := 0 to pred(ParentForm.ComponentCount) do
  begin
    if (ParentForm.Components[i].Name = ControlName) then
    begin
      if ShowControl then
        TControl(ParentForm.Components[i]).Show
      else
        TControl(ParentForm.Components[i]).Hide;

      Break;
    end;
  end;
end;

然后我尝试像这样使用它:

procedure TForm1.Button6Click(Sender: TObject);
begin
  ShowHideEveryControl(TForm(TForm1), 'Button4', True);
end;

为什么点击 Button6 时会出现访问冲突?

对我来说一切都很好...... Button4 作为一个孩子存在 :)

【问题讨论】:

    标签: delphi


    【解决方案1】:

    这个演员表是错误的:

    TForm(TForm1)
    

    您告诉编译器忽略TForm1 不是TForm 实例的事实,并要求它假装它是。这很好,直到您实际尝试将其用作实例,然后发生错误。

    您需要将真实实例传递给TForm 后代。你可以这样写:

    ShowHideEveryThing(Self, 'Button4', True);
    

    以防万一您对此不清楚,您的过程参数类型为TForm。这意味着您需要提供一个类的实例,该类要么是TForm,要么派生自TForm。我再说一遍,你必须提供一个实例。但是你提供TForm1 这是一个


    然后下一个问题来了:

    if (ParentForm.Components[i].Name = FormName) then
    begin
      if ShowForm then
        TForm(ParentForm.Components[i]).Show
      else
        TForm(ParentForm.Components[i]).Hide;
    
      Break;
    end;
    

    你再次使用了错误的演员表。当编译器告诉你一个特定的对象没有方法时,你必须听它。告诉编译器闭嘴并假装一种类型的对象实际上是另一种类型的对象是没有好处的。您的按钮绝对不是表单,因此不要尝试将其转换为 TForm

    很难知道您在这里实际想要做什么。当你写:

    ShowHideEveryThing(Self, 'Button4', True);
    

    在我看来,这样写更明智:

    Button4.Show;
    

    使用表示为文本的名称来引用控件并不是一个好主意。使用引用变量来引用它们更加安全和清晰。这样您就可以让编译器完成其工作并检查程序的类型安全性。


    您的函数中使用的名称是可疑的:

    procedure ShowHideEveryThing(const ParentForm: TForm; const FormName: String; 
      ShowForm: Boolean);
    

    让我们看看它们:

    • ShowHideEveryThing:但您声称该函数应该显示/隐藏一个元素。这与 everything 的使用不符。
    • FormName:您实际上传递了一个组件名称,然后查找 ParentForm 拥有的具有该名称的组件。看来FormName是错误的。
    • ShowForm:再说一遍,您是要控制表单的可见性还是单个元素?

    显然,您需要退后一步,明确此功能的意图。


    顺便说一句,你通常不需要写:

    if b then
      Control.Show
    else
      Control.Hide;
    

    你可以写:

    Control.Visible := b;
    

    不过,我给你的第一条建议是在你正确理解之前停止施法。一旦你正确理解它,尽可能设计你的代码,这样你就不需要强制转换。如果您确实需要强制转换,请确保您的强制转换有效,最好使用带有as 运算符的检查强制转换。或者至少先使用is 运算符进行测试。

    您的代码显示了典型错误的所有特征。编译器反对您编写的代码。您从某个地方了解到可以使用强制转换来抑制这些编译器错误,现在您广泛应用这种技术作为使您的程序编译的一种手段。问题是编译器总是知道它在说什么。当它反对时,听它。如果您发现自己使用强制转换来抑制编译器错误,请退后一步,仔细考虑一下您在做什么。编译器是你的朋友。

    【讨论】:

    • 解决方案正在运行,谢谢...我确实像:TControl(ParentForm.Components[i]).Visible 它应该适用于每个可视组件吗?
    • @user3243859 您必须强制转换才能使其编译的事实告诉您,它不适用于每个组件。只要组件源自TControl,它就可以工作。你确实需要检查一下。
    • 请问您为什么拒绝在Button6Click 中简单地写Button4.Show?我做过很多 GUI 开发,从来没有用他们的名字提到过控制。感觉你做错了。
    • 我正在研究通用程序解决方案...这只是 Button4 隐藏它的示例 :)
    • 为什么您觉得需要使用名称来识别您的控件?我的意思是,也许有一个很好的理由,但如果你能避免它,你应该这样做。也许您不知道如何避免它,或者没有意识到避免通过名称指定控件的好处。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-17
    • 1970-01-01
    相关资源
    最近更新 更多