NIL可以用于Sender对象吗?

4
最近我被要求自动化一个小的子程序,该程序呈现了一系列数据记录,并提供了四个潜在按钮中的任何两个供用户在查看记录分析后选择。老板说让用户看到分析结果是浪费时间的,因为用户总是选择按钮列表中的第一个选项,他准备接受我的猜测,除了对于他最好的用户外。因此,他想添加一系列新按钮,以提供“自动处理”、“手动处理”和“逐案处理”。最后一个按钮将只运行已经存在的代码。第二个按钮基本上什么也不做,然后退出。第一个按钮?那就麻烦了。
我的决定是使用一些标志,然后让自动路径模拟单击基于分析的最佳子按钮。问题在于,由于运行分析的过程名为RunAnalysis并未附加到特定对象以通过TObject传递,因此无法调用Button1Click(Sender)。最终,我将Button1Click方法的核心重构为Button1Pressed,然后从Button1Click调用它。因此,我能够从RunAnalysis内部调用Button1Pressed。
避免的路径将是调用Button1Click(Nil)。我没有尝试过它,因为我有一个简单的解决方案(顺便感谢Modelmaker)。但我的问题是,空引用是否有效,或者它是否会引起灾难。我能否调用一个具有发送器的更高函数(随机的?),只是为了在过程调用中拥有发送器对象呢?如果我不使用任何实际参考发送器的东西,那么发送器对象到底有多重要?
系统详细信息:Delphi 7 在Win 7编程环境中使用,用于Windows XP。
谢谢您提前给出的任何智慧,GM

1
你可以使用nil作为Sender参数来调用事件处理程序,前提是该事件处理程序不会尝试访问Sender对象。然而,你应该知道,在任何情况下最好不要直接调用事件处理程序。如果你想模拟按钮的按下,则应该简单地调用按钮的Click方法。 - David Heffernan
David,我无法调用click方法,因为没有Sender可以传递。即Button1Click(sender);出现错误。我考虑在RunAnalysis过程中从Button1Click(nil)中执行,但担心会发生灾难,最终将原始的Button1Click更改为调用Button1Pressed而不带任何参数。然后,我可以在RunAnalysis中更改对Button1Pressed的调用。在Button1活动处理程序中,我从未引用Sender。因此我的问题是什么。感谢您的评论并确认Nil在这种情况下是一个合法的参考。 - GM Mugford
不,我的意思是你有一个Button: TButton,然后写上Button.Click。你应该始终优先使用这种方式来调用事件处理程序,而不是直接调用它们。它们真的只应该由框架来调用。 - David Heffernan
每当我不需要给某个发送者东西时,我总是传递 nil。在你的情况下,只要你不假定在另一端它会被分配,那么这样做应该是安全的。但是,在某些情况下,你确实需要小心传递 nil - Jerry Dodge
4个回答

8

如果可能的话,我倾向于将事件处理程序的代码放入另一个方法中:

procedure TForm1.DoSomething(const Test: Boolean);
begin
  // Do Something based on Test
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  DoSomething(False); // init here
end;

procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  DoSomething(TCheckBox(Sender).Checked);
end;

因此,当我需要调用CheckBox1Click(nil)时,这是一个让我将代码从事件处理程序中分离出来的好迹象。


Kobik,最终我做的就是将Button1Click(sender)方法基本上简化为Button1Pressed,并将所有代码从click方法移动到pressed方法。这使我能够从没有Sender对象的过程中调用Button1Pressed。我实际上没有尝试使用nil参数,因为担心会搞砸一个需要昨天就完成的编程请求。但看起来nil确实可以工作,但正如你所说,这是不好的编程实践。感谢您的评论。 - GM Mugford
顺便提一下,你应该设定一个有意义的名称来描述这个方法,而不是仅仅使用“Button1Pressed”。 - kobik

6
回调函数或“事件”与任何其他函数没有区别。只要您(a)编写了该代码并知道它是空安全的,或者(b)已经阅读了该代码及其所有调用而没有检查nil,并且知道它是空安全的,就可以在任何地方传递NIL引用。
一些程序员自由地使用NIL,而有些人认为使用NIL参数值是不好的风格。我的风格倾向于“不要假设Sender已分配,并且不要假设它是特定类型”,这会导致我的事件处理程序中有大量的检查代码,但对于不同于我的代码的其他代码,情况千差万别,因此编码的第一条规则出现了:“不要做假设。阅读代码。”。

沃伦回复科比克说,让人感到欣慰的是我本来可以这样做,但选择了更好的编程解决方案,而且只需要很少的额外努力。感谢你的评论。 - GM Mugford

5
你可以将nil用作TNotify事件(或任何其他期望Sender的事件)的参数,只要代码不引用Sender即可。
procedure TForm1.Button1Click(Sender: TObject);
begin
  DoSomeStuff(nil);
end;

procedure TForm1.DoSomeStuff(Sender: TObject);
begin
  // Safe
  DoSomeOtherStuff;
  // Safe

  // Do stuff with Sender
  if Sender is TButton then
    TButton(Sender).Caption := 'In DoSomeStuff'

  // NOT safe!
  with TButton(Sender) do
  begin
    Caption := 'In DoSomeStuff';
  end;
end;

Ken,我没有以任何方式使用Sender,只有在我想从没有参数的过程中运行Button1Click过程时才涉及它。问题归结为NIL是否安全地替换调用需要Sender对象的过程(即Button1Click(Sender)),但我在任何代码中都没有使用Sender的过程。感谢您的评论。 - GM Mugford
我直接回答了那个问题。请查看DoSomeStuff函数中begin后面的第一行代码,它演示了安全地使用nil进行调用,只要您不引用Sender。另外两个演示了两种方式(使用和不使用nilSender)。这不是一个“注释”,而是对所提出问题的完整回答。 :) - Ken White

2

简短回答 - 是的。

我使用它来区分用户点击菜单事件还是直接由代码调用。


调度程序,感谢简明扼要。我喜欢您如何专门使用NIL进行区分。但是其他评论让我考虑重构并且不使用nil参数可能是适应这些情况的正确方式。感谢您的评论。 - GM Mugford
我看到你在上面对Ken的评论,他是正确的。他和我(虽然简短地)回答了这个问题 - 它是否安全?只要被调用的事件不对Sender做出假设,并且对sender内容的假设是不安全的,那么它就是安全的,无论如何。 - Despatcher

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接