如何动态地将条件和方法传递给递归方法
我想做一个这样的方法,
public dynamic Traverse(dynamic entity, conditions, method)
{
foreach (var propInfo in GetTraversableProperties(entity))
{
if (condition) method(propInfo.GetValue(etc));
Traverse(propInfo, condition, method);
}
return entity;
}
我该怎么做?将条件和方法作为参数传递的语法是什么?另外,将条件设为方法并检查它的返回值是否更有意义?
I want to make a method like this,
public dynamic Traverse(dynamic entity, conditions, method)
{
foreach (var propInfo in GetTraversableProperties(entity))
{
if (condition) method(propInfo.GetValue(etc));
Traverse(propInfo, condition, method);
}
return entity;
}
How can I do this? What is the syntax for passing the conditions and method as parameters? Also, does it make more sense to make the conditions a method and check the return value of it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
你的方法和加里的答案都是具体化对象图递归遍历的抽象概念的完全合理的方法。然而,我看到了四个潜在的问题。不知道您的确切场景,也许这些对您来说不是问题,或者也许您应该考虑它们:
首先,假设您正在遍历的图具有非常长的路径。您隐式地进行了深度优先遍历,并且即使在支持尾递归的体系结构上,您的方法也无法轻松地进行尾递归,因此您面临着耗尽调用堆栈的风险。
其次,您假设该图是非循环的;如果图表是循环的,那么您肯定会耗尽堆栈空间。
第三,我不明白为什么遍历算法会返回一个实体。为什么这个方法不是无效的?或者,如果您使用返回作为累加器来累加遍历计算的值,那么为什么递归步骤不对返回的实体执行某些操作?
第四,你的关注点分离似乎很糟糕。调用者负责确定(1)图的根是什么,(2)如何处理每个节点。但被调用者负责 (3) 找出要递归的对象。这对我来说似乎很奇怪。调用者提供起点;难道打电话的人不应该对如何继续下去也有发言权吗?
我通常按如下方式解决这个问题:
如果我想要一个累加器,我可能会实现类似这样的草图:
因此,例如,如果我有一个整数图,并且我希望对从特定起始节点可到达的节点求和,我会说:
但是,我会很想在“让我们分离我们的关注点”的道路上更进一步,然后说,嘿,让我们编写一个抽象遍历器:
现在,如果我想对图中的每个节点执行一些操作,我只是说:
如果我想要表达“对匹配条件的每个节点执行某些操作”的概念然后我会写:
Your approach, and Gary's answer are both perfectly reasonable ways to reify the abstract notion of recursive traversal of a graph of objects. However, I see four potential issues. Not knowing your precise scenario, perhaps these are non-issues for you, or perhaps you should consider them:
First, suppose the graph you are traversing has extremely long paths. You are implicitly doing a depth first traversal and your method cannot be made tail recursive easily even on architectures that support tail recursion, so you run the risk of running out of call stack.
Second, you presume that the graph is acyclic; if the graph is cyclic then you certainly will run out of stack space.
Third, I don't see why the traversal algorithm returns an entity. Why isn't this method void? Or, if you are using the return as an accumulator to accumulate the value computed by the traversal, then why does the recursive step not do something with the entity returned?
Fourth, you seem to have a bad separation of concerns here. The caller is responsible for determining (1) what the root of the graph is, (2) what to do with each node. But the callee is responsible for (3) figuring out what objects to recurse on. That seems bizarre to me. The caller is providing the starting point; shouldn't the caller also have some say in how to keep on going?
I generally solve this problem as follows:
If I wanted an accumulator I might implement something like this sketch:
So, for example, if I had a graph of integers and I wished to sum the nodes reachable from a particular starting node, I'd say:
However, I'd be tempted to go even one step further on the "let's separate our concerns" path and say, hey, let's just write an abstract traverser:
and now if I want to do some action to every node in the graph, I just say:
If I wanted to express the notion of "do something to each node that matches a condition" then I'd write:
我认为使用 Func 委托来满足条件是有意义的。至于如何传递方法,这又会用到委托。我会做类似的事情:
Func: http://msdn.microsoft.com/en -us/library/bb549151.aspx
操作:http://msdn.microsoft.com/en-us/library/018hxwa8.aspx
i think using a Func delegate for the conditions makes sense. As for how to pass methods, this again would use delegates. I would do something like:
Func: http://msdn.microsoft.com/en-us/library/bb549151.aspx
Action: http://msdn.microsoft.com/en-us/library/018hxwa8.aspx