Winforms 将 Control 转换为其他控件类型并分配特定的事件处理程序

Winforms cast Control to other control type and assign specific event handler

至于问题,我正在以编程方式创建控件,根据变量,我需要创建不同的控件类型,然后将它们添加到面板中。

当我尝试从 Control 转换 CheckBox 并添加 CheckedChanged 事件时,我得到“Control does not contain a definition for that [...]”。

我知道我可以复制 Panel.Add(Control) 行并避免使用公共控件,但我想知道是否可以从更通用的控件

中正确转换控件
          Control val;
          if (dataType.Equals(COIL))
          {
            val = new CheckBox
            {
              Name = $"txt{i}"
            };
            val.CheckedChanged += ChangeValue;
          }
          else
          {
            val = new TextBox
            {
              Name = $"txt{i}",
              Size = new Size(70, 20),
              TextAlign = HorizontalAlignment.Center,
              TabIndex = i
            };
          }

          flpValues.Controls.AddRange(new Control[] { lbl, val });

有什么问题?

您有这样的代码:

Control val;
// ...
val = new CheckBox
{
    Name = $"txt{i}"
};
val.CheckedChanged += ChangeValue;

它不起作用,因为 Control 中没有 CheckedChanged,而 val 尽管 CheckBox 被键入为 Control


如何解决?

你可以施法:

Control val;
// ...
val = new CheckBox
{
    Name = $"txt{i}"
};
((CheckBox)val).CheckedChanged += ChangeValue;

但是,在我看来,那是在做不必要的额外工作。我会改用另一个变量:

Control val;
// ...
var checkBox = new CheckBox
{
    Name = $"txt{i}"
};
checkBox.CheckedChanged += ChangeValue;
val = checkBox;

事实上,这可以很容易地将这些行提取到另一个方法中,让您:

Control val;
// ...
val = CreateCheckBox(i);

你能安全投出吗?

或许你更感兴趣的是能不能安全施法。毕竟,当您编写 ((CheckBox)val) 时,编译器不会检查类型是否正确,而是在运行时进行检查(如果失败则抛出,这不会发生)。

有几种安全转换的方法,从 as:

开始
Control val;
// ...
val = new CheckBox
{
    Name = $"txt{i}"
};
(val as CheckBox).CheckedChanged += ChangeValue;

现在,根据您的分析器,您可能会收到警告。由于使用 as 进行转换不会抛出,但可能 return 为 null。这在理论上会导致异常。当然,我们知道这不会发生以防万一(因此您可以抑制任何警告,如果有的话)。

另一种选择是使用 is:

Control val;
// ...
val = new CheckBox
{
    Name = $"txt{i}"
};
if (val is CheckBox checkBox)
{
    checkBox.CheckedChanged += ChangeValue;   
}

现在我引入了条件语句(我们知道它永远为真)和一个新变量。至少它没有警告。然而,很明显,我最初建议的仅使用附加变量绝对比这更好(因为该解决方案没有条件)。


编译器会优化吗?

可能你担心编译器会不会优化代码

代码 ((CheckBox)val) will result in a castclass opcode. (val as CheckBox) will result in a isinst opcode. Similarly if (val is CheckBox checkBox) 将导致 isinst,加上分支(我在 sharplab 中得到 brfalse.s)。

不是,JIT 编译器似乎也没有优化它。事实上,使用新变量的 the suggested solution 没有任何与转换或检查类型相关的操作码。它也是 JIT asm 中较短的代码。