C# LINQ与递归

C# LINQ与递归,c#,linq,recursion,C#,Linq,Recursion,考虑以下几点: public class Box { public BoxSize Size { get; set; } public IEnumerable<Box> Contents { get; set; } } Box FindBoxBySize(Box box, BoxSize size) { Box _foundBox = null; Action<IEnumerable<Box>> _recurse = nu

考虑以下几点:

public class Box
{
    public BoxSize Size { get; set; }

    public IEnumerable<Box> Contents { get; set; }
}

Box FindBoxBySize(Box box, BoxSize size)
{
    Box _foundBox = null;

    Action<IEnumerable<Box>> _recurse = null;

    _recurse = new Action<IEnumerable<Box>>(boxes =>
    {
        foreach (var _box in boxes)
        {
            if (_box.Size == size)
            {
                _foundBox = _box;

                return;
            }

            if (_box.Contents != null) _recurse(_box.Contents);
        }
    });

    _recurse(box.Contents);

    return _foundBox;
}
公共类框
{
public BoxSize大小{get;set;}
公共IEnumerable内容{get;set;}
}
长方体FindBoxBySize(长方体,长方体大小)
{
Box _foundBox=null;
动作_recurse=null;
_递归=新操作(框=>
{
foreach(框中的变量框)
{
如果(_box.Size==大小)
{
_foundBox=_-box;
返回;
}
如果(\u box.Contents!=null)\u递归(\u box.Contents);
}
});
_递归(box.Contents);
退货箱;
}
是否有任何方法可以使用LINQ压缩
FindBoxBySize()
?另外:欢迎对我的代码发表评论。我没有做太多的递归,所以我可能在实现中遗漏了一些东西。

扩展方法:

class Box
{
    public IEnumerable<Box> GetBoxes()
    {
        // avoid NullReferenceException
        var contents = this.Contents ?? Enumerable.Empty<Box>();

        // do the recursion
        return contents.SelectMany(b => b.GetBoxes()).Concat(contents);
    }

    public Box GetBox(int size)
    {
        return this.GetBoxes().FirstOrDefault(b => b.Size == size);
    }
}

如果您想使用LINQ,可以这样做(未经测试):

public static IEnumerable GetBoxesRecursive(此框)
{
如果(框==null)
抛出新的ArgumentNullException(“框”);
var children=box.Contents??Enumerable.Empty();
//在C#3.0中使用lambda
var recursiveChildren=children.SelectMany(GetBoxesRecursive);
返回新的[]{box}.Concat(recursiveChildren);
}
...    
Box desiredBox=myBox.GetBoxesRecursive()
.FirstOrDefault(b=>b.Size==desiredSize);

您可以通过编写

Box FindBoxBySize(Box box, BoxSize size)
{
    if (box.Size == size)
        return box;

    foreach (var child in box.Contents)
    {
        var foundBox = FindBoxBySize(child, size);
        if(foundBox != null)
            return foundBox;
    }

    return null;
} 

至于LINQ:LINQ并没有提供处理递归数据结构的好方法。通过向谷歌询问“LINQ递归”,可以找到几种不同的扩展方法来解决这个问题,但我不确定它们是否在这种情况下增加了清晰度。

我也会采用扩展方法,但使用迭代器方法:

public static class BoxEx
{
    public static IEnumerable<Box> Flatten(this Box box)
    {
        yield return box;
        if (box.Contents != null)
        {
            foreach (var b in box.Contents.SelectMany(b2 => Flatten(b2)))
            {
                yield return b;
            }
        }
    }
}
您的原始调用代码无需修改即可工作:

var small = FindBoxBySize(box, BoxSize.Small);

我想是的。但请注意我的中断声明;我真的希望有尽可能少的“循环”,所以无论何时找到匹配,我都希望终止。这仍然是错误的。它在
null
上爆炸,这显然对OP很重要。其次,它实际上从未产生真正的
框。什么时候真的会产生一个方框引用?@Ani:是的,我明白了。但我真的没有检查过。实际上,我在重写自己的扩展方法(对于ASP.NET,对于
Control.FindControl()
)的递归,但没有注意到:)此外,为自己的类编写扩展方法也不是一个好的做法。既然可以将EM作为Box类的公共实例方法,为什么还要使用它呢?作为继承的面向对象的概念不应该轻易被EM取代。我真的很喜欢EM,当时写了很多。当您想扩展外部库的类型(包括.net framework本身)而不想(或不能)创建派生类型时,可以使用这种技术。但是过度使用这种技术会导致一个不是面向对象的模型,这通常是不可取的。问候语。
public static class BoxEx
{
    public static IEnumerable<Box> Flatten(this Box box)
    {
        yield return box;
        if (box.Contents != null)
        {
            foreach (var b in box.Contents.SelectMany(b2 => Flatten(b2)))
            {
                yield return b;
            }
        }
    }
}
Box FindBoxBySize(Box box, BoxSize size)
{
    return (from b in box.Flatten()
            where b.Size == size
            select b).FirstOrDefault();
}
var small = FindBoxBySize(box, BoxSize.Small);