【问题标题】:Removing from list recursively in C#在 C# 中递归地从列表中删除
【发布时间】:2026-01-31 01:25:01
【问题描述】:

我有以下递归函数,用于向下搜索分层树并从列表中删除找到的对象:

private List<Tag> RemoveInvalidTags(Device device, List<Tag> tags)
{
   var childDevices = device.ChildDevices.Select(c => c.ChildDevice);

   foreach (var child in childDevices)
   {
      tags.Remove(child.Tag);
      RemoveInvalidTags(child, tags);
   }

   return tags;
}

我希望这样做是从标签列表中删除此级别的所有子设备标签,为您的孩子递归调用该函数,然后将该列表返回到上一级。

这会通过引用传递标签列表并修改原始传递列表吗?或者我应该按照

的方式做一些事情
validTags = CollectValidTags(child, tags);

并将所有返回的列表加起来?

【问题讨论】:

    标签: c# .net recursion


    【解决方案1】:

    这是否会通过引用传递标签列表

    没有。 list 对象 是“按值”传递的(但请参见下一个)。 (在 C# 中 "pass by reference" 需要 refout ,但这里没有这样做,也不需要这样做。)

    并修改原来的通过列表?

    是的。这是因为 list 对象 被传递了。 那个列表对象是变异的。传递引用类型(使用class 定义的任何内容)从不 隐式地进行复制/克隆/复制。对象就是它。

    现在,回到“按值传递”:“传递的值”是“引用”的值(内部,无需关心):这种调用策略在语言中更好地称为Call/Pass By Object Sharing像 C#。 同一个对象共享的(就像它被分配给两个不同的变量一样)。 (值类型 -- struct -- 不同之处在于它们(经常)在堆栈上被复制/复制,但 List&lt;T&gt;class。)

    或者我应该按照

    的方式做一些事情

    这取决于所需的语义。调用者期待是直接还是间接的副作用?突变的副作用会导致意想不到的情况吗?确保以任何一种方式记录它。 (我更喜欢保证初始对象不发生变异的方式。)

    希望能解决一些问题。

    编码愉快。

    【讨论】:

    • 请解释一下-1。我不介意,但没有解释也没用。
    • 我在这里唯一的挑剔是您如何引用按值传递的“列表对象”。这充其量是误导。对象是堆上的东西。引用是按值传递的,是的,但继续说 list object 让我觉得很奇怪。顺便说一句,我再次阅读后删除了 -1 。由于我第一次提到的问题,我有点误解了你。
    • @Ed S. 我希望编辑可以清除一些。我试图专注于一个事实,而不是一个对象......本身,无论它使用什么名称(变量)。不过,它仍然开始有点强。
    • +1 我发现对象按值传递的想法是ref 最有用的想法。
    • 对象引用按值传递更准确。引用类型变量保存对对象的引用,该引用是被传递的内容,无论是通过值还是通过引用。
    【解决方案2】:

    在您的代码中,您正在修改 tags 参数中的项目并将修改后的列表作为结果传回。您希望避免以这种方式修改列表 - 尤其是在循环内部,在许多情况下它可能会让您感到悲伤。

    我有一个基于 LINQ 的替代方案。

    如果我了解您的代码意图,您希望执行以下操作:

    Func<Device, IEnumerable<Device>> flatten = null;
    flatten = d =>
    {
        return (new [] { d }).Concat(
            from c in d.ChildDevices
            from f in flatten(c)
            select f);
    };
    
    var collectedValidTags = flatten(device).Select(d => d.Tag);
    
    var result = tags.Except(collectedValidTags).ToList();
    

    这种方法不会传递您的标签列表,因此没有机会修改您的原始列表。

    这有帮助吗?

    【讨论】:

    • 我无法选择你的答案作为正确答案,因为它实际上并没有回答我原来的问题,但是我给了你一个赞成票,因为你的答案是一个更好的解决方案。干杯。
    • @link664 - 完全理解。我没想到会直接回答您的问题,而是为您提供一种干净、实用的方式来做您想做的事,而不会弄乱可变集合。
    【解决方案3】:

    简短的回答 - 你的代码会做你想做的事。

    长答案 - 您应该阅读有关 ref 关键字作用的说明。我建议您阅读尽可能多的描述;有很多不同的方式来表达它(“我喜欢把它想象成......”),有些对你有用,有些则不行。如果您阅读了许多描述(来自理解它的人),那么您应该对某种理解有所了解。

    下面是一个帮助您入门的列表:

    【讨论】: