为什么 i.Parent.ReplaceWith(i) 不抛出异常?
why does i.Parent.ReplaceWith(i) not throw an exception?
在这些语句中 (运行 MoreLinq
):
var xml = @"
<div>
<p>
<h2>hey</h2>
</p>
<pre />
<h2 class=""cool"" />
<p>
<h2>okay</h2>
</p>
</div>
".Trim();
var div = XElement.Parse(xml);
var h2Elements = div.Descendants("h2");
h2Elements.ToList().ForEach(i =>
{
if(i.Parent.Name != "p") return;
i.Parent.ReplaceWith(i);
});
我看到 i.Parent.ReplaceWith(i)
不会抛出异常,但这会抛出空引用异常(使用 MoreLinq
中的 ForEach
):
h2Elements.ForEach(i =>
{
if(i.Parent.Name != "p") return;
i.Parent.ReplaceWith(i);
});
我知道 LINQ 的 ToList()
正在制作列表的副本,但副本不会也抛出异常吗?另外,这里是否存在某种孤立引用的内存泄漏?
您根本不需要 MoreLINQ 来演示这一点 - 您也可以简化示例代码:
using System;
using System.Linq;
using System.Xml.Linq;
class Program
{
static void Main()
{
var element = new XElement(
"root",
new XElement("parent", new XElement("child")),
new XElement("parent", new XElement("child"))
);
var children = element.Descendants("child");
foreach (var child in children.ToList())
{
child.Parent.ReplaceWith(child);
}
}
}
如果没有 ToList
调用,则会抛出 NullReferenceException
。对于 ToList()
调用,也不例外。例外情况是:
Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
at System.Xml.Linq.XContainer.<GetDescendants>d__39.MoveNext()
at Program.Main()
基本上,您通过在遍历树的同时修改树来使查询无效。这有点像在迭代 List<T>
时调用 Add
或 Remove
,但 LINQ to XML 更难发现问题并抛出有意义的异常。重要的是要注意调用 ReplaceWith
时不会出现异常 - 这是 iteration 失败的部分,因为它无法在你之后正确地遍历树修改了。
当您调用 ToList()
时,您只是在列表中获得单独的 XElement
值 - 当您遍历该列表时,对元素的任何更改都不会更改出现的引用在列表中。
至于内存泄漏:不,这就是垃圾收集器的用途...
在这些语句中 (运行 MoreLinq
):
var xml = @"
<div>
<p>
<h2>hey</h2>
</p>
<pre />
<h2 class=""cool"" />
<p>
<h2>okay</h2>
</p>
</div>
".Trim();
var div = XElement.Parse(xml);
var h2Elements = div.Descendants("h2");
h2Elements.ToList().ForEach(i =>
{
if(i.Parent.Name != "p") return;
i.Parent.ReplaceWith(i);
});
我看到 i.Parent.ReplaceWith(i)
不会抛出异常,但这会抛出空引用异常(使用 MoreLinq
中的 ForEach
):
h2Elements.ForEach(i =>
{
if(i.Parent.Name != "p") return;
i.Parent.ReplaceWith(i);
});
我知道 LINQ 的 ToList()
正在制作列表的副本,但副本不会也抛出异常吗?另外,这里是否存在某种孤立引用的内存泄漏?
您根本不需要 MoreLINQ 来演示这一点 - 您也可以简化示例代码:
using System;
using System.Linq;
using System.Xml.Linq;
class Program
{
static void Main()
{
var element = new XElement(
"root",
new XElement("parent", new XElement("child")),
new XElement("parent", new XElement("child"))
);
var children = element.Descendants("child");
foreach (var child in children.ToList())
{
child.Parent.ReplaceWith(child);
}
}
}
如果没有 ToList
调用,则会抛出 NullReferenceException
。对于 ToList()
调用,也不例外。例外情况是:
Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
at System.Xml.Linq.XContainer.<GetDescendants>d__39.MoveNext()
at Program.Main()
基本上,您通过在遍历树的同时修改树来使查询无效。这有点像在迭代 List<T>
时调用 Add
或 Remove
,但 LINQ to XML 更难发现问题并抛出有意义的异常。重要的是要注意调用 ReplaceWith
时不会出现异常 - 这是 iteration 失败的部分,因为它无法在你之后正确地遍历树修改了。
当您调用 ToList()
时,您只是在列表中获得单独的 XElement
值 - 当您遍历该列表时,对元素的任何更改都不会更改出现的引用在列表中。
至于内存泄漏:不,这就是垃圾收集器的用途...