Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object type not recognised on foreach loop

Tags:

c#

winforms

So, i ran into a strange problem today.

Let's say i have a TreeNode defined like this:

TreeNode node = new TreeNode();
node.Nodes.Add(new TreeNode { Name = "aaa" });
node.Nodes.Add(new TreeNode { Name = "bbb" });

And then I call a recursive method

ColorNode(node.Nodes, Color.Green);

The method looks like this:

void ColorNode(TreeNodeCollection nodes, System.Drawing.Color Color)
{
    foreach (var child in nodes)
    {
        child.ForeColor = Color;
        if (child.Nodes != null && child.Nodes.Count > 0)
            ColorNode(child.Nodes, Color);
    }
}

In that foreach loop if I keep var child Visual Studio cries that

Object does not contain a definition for ForeColor and no extension method ForeColor accepting a first argument of type ojbect could be found.

object does not contain a definition for Nodes and no extension method Nodes accepting a first argument of type object could be found.

But if i change var child with TreeNode child everything works as expected.

Can someone explain this behaviour?

like image 646
Zippy Avatar asked Jul 29 '26 01:07

Zippy


2 Answers

Because TreeNodeCollection has only got an enumerator on object (it implements IEnumerable, not IEnumerable<TreeNode>), the var becomes object. Type the variable yourself and your code will compile:

foreach (TreeNode child in nodes)
{ }
like image 155
Patrick Hofman Avatar answered Jul 30 '26 14:07

Patrick Hofman


As is stated in the other answers, TreeNodeCollection is a collection of object objects. This is because WinForms was a part of the C# language from the beginning (.NET 1.1), and generics weren't added until .NET 2.0. For backwards compatibility reasons, they did not alter the class to also implement IEnumerable<TreeNode>.

As you discovered, the only way to process them is by explicitly typing the objects as TreeNode within your foreach loop. That is because var wasn't added until .NET 3.5.

One other alternative (though more verbose) would be to get the objects of the type that you want:

foreach (var child in nodes.OfType<TreeNode>())

As I said, this is more verbose, and you would be better suited to just explicitly cast the objects. However, the OfType method is a good one to have in your repertoire.

Clarification

As mentioned in comments on Patrick's answer, another option is .Cast<T>(). However, if you do that, exceptions are thrown if an item cannot be cast to the specified type. In a situation like this, you are fine because based on the way that the collection is created, you won't have anything that is not a TreeNode. But, in other collections, it is possible to have other types based on super classes, etc. OfType<T>() will ignore anything that is not of the type you are requesting.

like image 35
krillgar Avatar answered Jul 30 '26 14:07

krillgar