The code below loops over an XmlNodeList and prints the text content in them. It works as expected:
using System;
using System.Threading.Tasks;
using System.Xml;
namespace cabinet;
public class Program
{
public static void Main(string[] args)
{
XmlNodeList nodeList = XMLUtils.GetXPathNodes("./data/xml/cabinet.xml", "//cab:numéro");
foreach (XmlNode node in nodeList)
{
Console.WriteLine(node.InnerText);
}
}
}
However, if I change the XmlNode in the foreach loop into a var
the code no longer works and it generate an error.
This does not work:
foreach (var node in nodeList)
{
Console.WriteLine(node.InnerText);
}
The error is:
'object' does not contain a definition for 'InnerText' and no accessible extension method 'InnerText' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)
From reading a little bit online and from the error message I guess that the compiler cannot figure out the type of the node
variable so it puts object
. But shouldn't this work because of polymorphism? Meaning that even if the exact type can't be found, the type Object should still work, no?
Please correct if I am wrong about the Object thing, and please explain why putting var here doesn't work.
As @itsme86's comment explains, it's because XmlNodeList
implements IEnumerable
and not IEnumerable<T>
.
Why does this matter? IEnumerable
's GetEnumerator()
is non-generic. This means that it isn't an enumerator for a specific type, it's an enumerator for object
(because anything can be casted to object
, even primitives). This means that your var
is actually of type object
, which lacks an InnerText
member, which is why you get the error.
foreach (XmlNode node in nodeList)
does work however because foreach
effectively does an explicit cast at runtime. This means that node
truly is an XmlNode
, so you can access its InnerText
member. It's effectively equivalent to:
foreach (object obj in nodeList) {
XmlNode node = (XmlNode) obj;
...
}
You can actually prove that this is a runtime cast by making node
a different type. If you instead make node
a string
, it would still compile:
foreach (string node in nodeList)
{
Console.WriteLine(node);
}
...but it wouldn't work at runtime:
Unhandled exception. System.InvalidCastException: Unable to cast object of type 'System.Xml.XmlNode' to type 'System.String'.
at cabinet.Program.Main(String[] args)
Command terminated by signal 6
If XmlNodeList did implement IEnumerable<XmlNode>
, then using var
instead of XmlNode
would have worked fine; and the string node in nodeList
example would instead throw a compile-time error:
Cannot convert type XmlNode to string