简体   繁体   中英

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

As for question I'm creating controls programmaticallya nd depending on variable I need to create different control types and then add them to a panel.

When I try to cast a CheckBox from a Control and add the CheckedChanged event, I get "Control does not contain a definition for that [...]".

I know I can just duplicate the Panel.Add(Control) line and avoid using a common control, but I was wondering if it is possible to correctly cast a control from a more generic 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 });

What is the problem?

You have code like this:

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

It does not work because there is no CheckedChanged in Control , and val despite being CheckBox is typed as Control .


How to fix it?

You could cast it:

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

However, in my opinion, that is doing unnecessary extra work. I'd use another variable instead:

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

In fact, that could easily extract those lines to another method, leaving you with:

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

Can you cast it safely?

Perhaps you are more interested in whether or not you can safely cast it. After all, when you write ((CheckBox)val) the compiler does not check if the type is correct, instead that check happens at runtime (and throw if it fails, which won't happen).

There are a few ways to do safe casts, starting with as :

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

Now, depending on your analyzers, you could have a warning. Since casting with as will not throw, but may return null. Which would - in theory - result in an exception. Of course, we know that won't happen in case (so you can suppress any warnings, if you have them).

Another option would be to use is :

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

Now I have introduced both a conditional statement (which we know will always be true) and a new variable. At least it has no warnings. However it should be evident that just having the additional variable as I first suggested is strictly better than this (since that solution has no conditional).


Will the compiler optimize it?

Perhaps you worry about whether or not the compiler can optimize the code.

The code ((CheckBox)val) will result in a castclass opcode. (val as CheckBox) will result in a isinst opcode. Similarly if (val is CheckBox checkBox) will result in isinst , plus branching (I got brfalse.s in sharplab).

An not, the JIT compiler does not seem to optimize it either. In fact the suggested solution that uses a new variable does not have any opcodes related to casting or checking the type. It is also the shorter code in JIT asm.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM