Now, why does XmlNode.SelectNodes(string xpath) return null if it doesn’t find anything?
I have some existing code that reads some parameters from XML, and passes some objects and those parameters into a method that filters those objects into two sets – one that is sensitive in some way to those parameters, and one that’s not.
FilterProducts( XmlNodeList processes, XmlNodeList processAttributes, out List<Product> sensitiveFolio, out List<Product> nonSensitiveFolio);
I’m drawing the list of processes and processAttributes from XML – and in some cases people don’t define any attributes, wanting to just build a folio sensitive to a list of processes. In this case the XmlNodeList returned is null – and when I pass that to FilterProducts it blows up.
After the null pattern, then, I figured what I wanted to do was actually pass an empty XmlNodeList rather than a null. FilterProducts could then just pass over the attributes, and work out what was dependent on the processes only. Then I realised that what I really needed to do was dig through FilterProducts and refactor it because it was doing too much – at least compared to the usage pattern was now different to that which originally formed it.
However; both these can happen in sequence. I could deliver the simpler – using the existing code and an empty list, which wouldn’t affect any other code, then taking a look at refactoring FilterProducts, which might affect other parts of the codebase. So, populate processAttributes with a successully parsed XmlNodeList, or a new EmptyNodeList if that's null:
XmlNodeList processAttributes = myConfigNode.SelectNodes("/processAttributes") ?? new EmptyNodeList;
and then defined EmptyNodeList by subclassing XmlNodeList:
private class EmptyNodeList : XmlNodeList { public override XmlNode Item(int index) { throw new NotImplementedException(); } public override IEnumerator GetEnumerator() { return new EmptyNodeListEnumerator(); } public class EmptyNodeListEnumerator : IEnumerator { public bool MoveNext() { return false; } public void Reset() { throw new NotImplementedException(); } public object Current { get { throw new NotImplementedException(); } } } public override int Count { get { return 0; } } }
That works fine, delivering some code, and leaving a look at refactoring some code used in multiple places for later.