Why does XElement not behave like a reference type?

Viewed 207

I noticed that XElement is a class, so I tried something like:

var doc = new XDocument(
    new XDeclaration("1.0", "utf8", "yes"),
    new XElement("Root")
    );
var root = doc.Root;
var com = new XElement("Component", new XAttribute("name", "arm"));
root.Add(com);
root.Add(com);
root.Add(com);
com.Add(new XAttribute("type", 1));

Console.WriteLine(doc);

but the output is:

<Root>
  <Component name="arm" type="1" />
  <Component name="arm" />
  <Component name="arm" />
</Root>

I also tried SetAttributeValue(), and got the same result.

Why is the type attribute only attached to the first component?

2 Answers

I've given a second answer below - better because it gives a concrete design reason why - but left this one because it evidences that internal copy has taken place.


Being a class does not give a clue as to how that class behaves or should be treated.

Using the debugger

Hashcodes per node

Each Node has a different hash and must therefore be assumed to be a different object (so presumably there is copying going on during Add()).

If you can change the order of your operations, this fixes the problem

static void X()
{
  var doc = new XDocument(
    new XDeclaration("1.0", "utf8", "yes"),
    new XElement("Root")
  );
  var root = doc.Root;
  var com = new XElement("Component", new XAttribute("name", "arm"));
  com.Add(new XAttribute("type", 1));
  root.Add(com);
  root.Add(com);
  root.Add(com);

  Console.WriteLine(doc);
}

giving

<Root>
  <Component name="arm" type="1" />
  <Component name="arm" type="1" />
  <Component name="arm" type="1" />
</Root>

My original answer stands (essentially "by design") and here is a reason why...

From MS Documentation (and following the relevant links) you will find that

  • XElement inherits XContainer inherits XNode
  • XContainer has method Add() and properties FirstNode and LastNode
  • XNode has properties NextNode and PreviousNode

If Add() blindly added references to the same object without creating copies where necessary to avoid multiple-referencing, how could circular references be avoided? In your example above, FirstNode and FirstNode.NextNode would reference the same object.

Related