开发者

LINQ Casting issue

I have a winform with two checkboxes and a button. On the CheckedChanged event of both checkboxes i had given the following code.

//Enable the button if any of the checkbox is checked
var ChkBoxes = from CheckBox ctrl in this.Controls 
开发者_运维技巧               where ctrl is CheckBox select ctrl;
button1.Enabled = ChkBoxes.Any(c => ((CheckBox)c).Checked);

but when checking either of the checkboxes I am getting an error "Unable to cast object of type 'System.Windows.Forms.Button' to type 'System.Windows.Forms.CheckBox'." the error comes up while executing the second line of code.

Later I updated the code to the following, which works fine. The only change I made is modified ctrl type from CheckBox to Control.

var ChkBoxes = from Control ctrl in this.Controls 
               where ctrl is CheckBox select ctrl;
button1.Enabled = ChkBoxes.Any(c => ((CheckBox)c).Checked);

My question is, in both cases I am returning controls only of type checkbox, then how come the cast error comes up. Can anyone explain me how this works?


Instead of using:

var ChkBoxes = from CheckBox ctrl in this.Controls where ctrl is CheckBox select ctrl;

Try using Enumerable.OfType<T> to do your filtering:

var chkBoxes = this.Controls.OfType<CheckBox>();
button1.Enabled = chkBoxes.Any(c => c.Checked); // No cast required now


Because it gets converted to something like this:

IEnumerable<CheckBox> ChkBoxes = this.Controls.Cast<CheckBox>().Where(ctrl => ctrl is CheckBox).Select(o => o);

Note that the cast to CheckBox gets executed before the check that ensures it's valid (the error occurs on the last line due to deferred execution).

To fix this, specify Control rather than CheckBox as you mentioned, or, even more succinctly, use OfType():

var ChkBoxes = this.Controls.OfType<CheckBox>();


Because 'this.Controls' holds Controls, all of them, on the form. The where clause has not yet been applied.

So before Linq can work with your where clause, it tried to cast the object to the type you specified, which was originally CheckBox. When it got to a Button, the cast failed.

In your fix, you correctly used the Control type, which is valid since all objects in 'this.Controls' are Control, and then Linq can apply the rest of the Linq statement.


In the first example the exception isnt thrown because a Linq query uses what's called "Deffered" or "Lazy" evaluation.

It only started getting values for you when the second line was run, called "Enumerating" the query.

Edit - The OfType extension method from the other answer is probably the way to go.


The Controls collection is an IEnumerable which holds Controls of different types not necessarily a CheckBox.

You need to cast to some common base type of these controls (Control) then filter the collection to CheckBox objects.

var query = from Control ctrl in this.Controls
            where ctrl is CheckBox
            select (CheckBox)ctrl; // cast to CheckBox here instead

// which is equivalent to:
var q = this.Controls
            .Cast<Control>()
            .Where(ctrl => ctrl is CheckBox)
            .Select(ctrl => (CheckBox)ctrl);

Or filter it directly using the OfType<T>() extension method. The result is a strongly typed enumerable IEnumerable<CheckBox>. I would recommend this approach.

var query = this.Controls.OfType<CheckBox>();
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜