使用Delphi中的Case...of语句时,Sender作为TObject出现了什么问题?

3
我想在我的 case ... 语句中使用 Sender 作为 TObject 的选择条件。
procedure TForm.ShowGUI (Sender: TObject);
begin
  case sender of
      ToolButton1: begin
          do_something;         
        end;

      ToolButton2: begin
          /// 
        end;

      ToolButton3: begin
          do_stufff_here; 
        end;

      ToolButton3: begin 
          /// 
        end;

      else ;

  end;
end;

一个技巧,使发送者成为案例语句所要求的序数类型?

一个技巧是创建动作来显示不同的GUI类型,并将每个动作分配给适当的按钮。 - idursun
Delphi应该开始更加灵活地处理case语句了。他们可以在后台自动将case语句转换为一系列的if语句,这样我们就可以使用非常量表达式和字符串了。这会节省我们手动转换的工作,因为我们无论如何都要这样做。 - Cosmin Prund
@Cosmin:在EMBA论坛上,一年前曾经讨论过Delphi中的模式匹配。如果我没记错的话,Nick Hodges甚至将其作为Delphi II语言的可能性。 - Arioch 'The
2个回答

10

你可以通过将Sender转换为整数(例如:NativeUInt(Sender))轻松将其转换为序数,但这样做没有任何好处,因为case语句需要“分支”的常量表达式,而ToolButton1ToolButton3不是常量,它们是变量。

例如,你可以针对按钮的属性进行一个case;例如,为每个ToolButton赋予有意义的Tag属性。然后,你可以执行类似以下的操作:

case (Sender as TToolButton).Tag of
  1: ; // ToolButton1 was pressed
  2: ; // ToolButton2 was pressed        
end;

根据David的建议: 如果你使用的控件没有直接关联到它功能的属性(非常不可能),那么一个简单的连续if语句可能更容易阅读。像我第一条建议中的Tag属性需要特别配置,它只是引入错误的另一种方式。例如:你可能复制粘贴了一个按钮但忘记了改变它的Tag属性,现在你有两个具有相同Tag属性的按钮。

首先,你应该为所有在代码中使用的组件重新命名,给它们适当的名称。然后你可以写出类似下面这样的代码:

if (Sender = tbAlignTextLeft) then
  begin
     // The tool-button for left text alignment was pressed
  end
else if (Sender = tbAlignTextCenter) then
  begin
    // Center-alignment button was pressed
  end
else
  begin
    // This would be the default action. If it were me, I'd raise an exception here.
  end

2
如果您这样做,我建议您在代码中设置标签,而不是在对象检查器中,并使用常量而不是数字。这可能会使将来的维护更容易。 - Keith Miller
3
虽然你可以这样做,但使用if语句会更简洁。 - David Heffernan
@Smasher 仅当你假装没有编写魔术常数并且研究“Tag”属性时才可以使用case选项。如果您考虑整个情况,那么case选项会大大失利。 - David Heffernan
1
@Smasher 是的,它很容易阅读。但那不是我的重点。显然我表达得不好。我对此的问题在于你必须在某个地方为每个控件设置“Tag”。并将其与case语句中的枚举相关联。现在,如果这是在.dfm文件中完成的,你如何维护它呢? - David Heffernan
好的,我现在明白你的观点了。你必须保持case和“标记”同步。我只是考虑可读性。 - jpfollenius
显示剩余4条评论

6
考虑为您的控件添加一些运行时支持,以关联枚举类型。首先创建一个枚举类型:
type
  TControlID = (cidAlignTextLeft, cidAlignTextRight, ....);

然后在您的表单中添加一个字典:

FControlIDs: TDictionary<TControl, TControlID>;

然后在表单的构造函数中创建并填充字典:

FControlIDs := TDictionary<TControl, TControlID>.Create;
FControlIDs.Add(tbAlignTextLeft, cidAlignTextLeft);
FControlIDs.Add(tbAlignTextRight, cidAlignTextRight);
....

这段代码应该放在一个单独的本地辅助函数中,而不是直接内嵌在表单构造函数中。

现在您可以编写您的 case 语句:

case FControlIDs[Sender as TControl] of
cidAlignTextLeft:
  ....
cidAlignTextRight:
  ....

你可以将TControlID塞入Tag中,然后将Tag强制转换为TControlID以适用于case语句。这与字典查找几乎相同。 - Cosmin Prund
1
@CosminPrund 我对Tag这个东西非常厌恶。使用它让我感觉很糟糕,就像在编写VB6一样。如果你忘记为其中一个控件分配Tag,它会默认为cidAlignTextLeft - David Heffernan
cidUnassigned 作为第一个枚举元素以防止出现问题。 - Cosmin Prund
@CosminPrund 当然可以这样做。我喜欢类型安全。我不喜欢 Tag。我也不喜欢 Sender: TObject,但是对此无能为力。至少 as 转换可以强制执行运行时类型安全。 - David Heffernan

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