开发者

Removing from list recursively in C#

I have the following recursive function that is used to search down a hierarchical tree and remove found objects from a list:

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;
}

What I am expecting this to do is remove all child device tags at this level from the tags list, call the function recursively for your children, then return that list up to the previous level.

Will this pass the tags list by reference and modify the original passed list? Or should I be doing something along the lines of

validTags = CollectValidTags(child开发者_开发百科, tags);

and adding up all the returned lists?


Will this pass the tags list by reference

No. The list object is passed "by value" (but see next). (ref or out is required to "pass by reference" in C#, but that is not being done here, nor does it need to be.)

and modify the original passed list?

Yes. This is because the list object is passed. And that list object is mutated. Passing a reference type (anything defined with class) never implicitly makes a copy/clone/duplicate. An object is what it is.

Now, back to "pass by value": the "value passed" is the value of the "reference" (internal, no need to concern with this): this calling strategy is better known as Call/Pass By Object Sharing in a langauge like C#. The same object is shared (just as if it were assigned to two different variables). (Value types -- a struct -- are different in that they (often) are copied/duplicated on the stack, but a List<T> is a class.)

Or should I be doing something along the lines of

It depends upon the desired semantics. Is the caller expecting the side-effects directly or indirectly? Can the mutation side-effect lead to unexpected scenarios? Make sure to document it either way. (I prefer the way that guarantees the initial object is not mutated.)

Hope that clears some things up.

Happy coding.


In your code you are modifying the items in your tags parameter and passing back the modified list as your result. You want to avoid modifying lists in this way - especially inside loops where it can cause you grief in many situations.

I have a LINQ-based alternative for you.

If I understand the intent of your code you want to do something like this:

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();

This approach doesn't pass your list of tags around so there is no chance of modifying your original list.

Does this help?


Short answer - your code will do what you want.

Long answer - you should read descriptions of what the ref keyword does. I would suggest you read as many descriptions as possible; there are many different ways to articulate it ("I like to think of it as... ") and some will work for you whilst others won't. If you read many descriptions (from people who understand it) then some kind of understanding should gel for you.

Here's a list to get you started:

  • Use of 'ref' keyword in C# (my answer)
  • C# ref keyword usage
  • Passing by ref?
  • Example of practical of "ref" use
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜