我有以下递归函数,用于搜索分层树并从列表中删除找到的对象:
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);
并将所有返回的列表相加?
答案 0 :(得分:3)
这会通过引用传递标签列表
没有。 列表对象按“值”传递(但请参阅下一步)。 C#中"pass by reference"需要ref
或out
,但这里没有完成,也不需要。{/ p>
并修改原始传递的列表?
是。这是因为传递了列表对象。 列表对象发生了变异。传递引用类型(使用class
定义的任何内容)从不隐式地复制/克隆/复制。一个对象就是它。
现在,回到“按值传递”:“传递的值”是“引用”的值(内部,无需关注此内容):此调用策略更好地称为Call/Pass By Object Sharing in像C#这样的语言。 同一对象是共享(就像它被分配给两个不同的变量一样)。 (值类型 - struct
- 不同之处在于它们(经常)在堆栈上被复制/复制,但List<T>
是class
。)
或者我应该按照
的方式做些什么
这取决于所需的语义。调用者是否期望直接或间接地产生副作用?突变副作用会导致意外情况吗?确保以任何一种方式记录。 (我更喜欢保证初始对象不会发生变异的方式。)
希望清除一些事情。
快乐的编码。
答案 1 :(得分: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();
此方法不会传递您的标记列表,因此无法修改原始列表。
这有帮助吗?
答案 2 :(得分:0)
简短回答 - 您的代码将按您的要求执行。
答案很长 - 您应该阅读ref
关键字的内容说明。我建议你阅读尽可能多的描述;有许多不同的方式来表达它(“我喜欢把它想象成......”),有些会为你效劳,而有些则不会。如果您阅读了许多描述(来自理解它的人),那么某种理解应该适合您。
这是一个让你入门的清单: