2

在这些语句中(使用 运行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)不会引发异常,但这会引发空引用异常(使用ForEachfrom MoreLinq):

h2Elements.ForEach(i =>
{
    if(i.Parent.Name != "p") return;
    i.Parent.ReplaceWith(i);
});

我知道 LINQToList()正在制作列表的副本,但副本不会也抛出异常吗?此外,这里是否发生了某种孤立引用的内存泄漏?

4

1 回答 1

4

您根本不需要 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就会抛出 a。有了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()

基本上,您通过在迭代树时修改树来使查询无效。这有点像 call Addor Removeon List<T>while 迭代它,但 LINQ to XML 更难发现问题并抛出有意义的异常。重要的是要注意调用时不会出现异常ReplaceWith- 这是失败的迭代部分,因为在您修改它之后它无法正确找到遍历树。

当您调用 时ToList(),您只是XElement在列表中获取单独的值 - 当您遍历该列表时,对元素的任何更改都不会更改列表中出现的引用。

至于内存泄漏:不,这就是垃圾收集器的用途......

于 2017-02-16T09:15:54.330 回答