问题描述
因此,我正在创建一个BaseIFrame
类,以从任何页面通用解析嵌套的iFrame,而无需利用ID,xpath或任何特定于DOM的类。我使用了递归功能来捕获页面上的所有iFrame,并通过堆栈进行收集。但是,在调试之后,我发现递归函数在遇到堆栈溢出之前一遍又一遍地遍历同一Web元素。
我该如何纠正?
BaseIFrame
类:
namespace [Confidental namespace] {
public class BaseIFrame {
protected IWebDriver _driver;
private ReadOnlyCollection<IWebElement> _iframes;
public BaseIFrame(IWebDriver _driver) {
this._driver = _driver;
_iframes = _driver.FindElements(By.TagName("frame"));
_iframesSize = _iframes.Count();
}
public ReadOnlyCollection<IWebElement> getFrames() {
return _iframes;
}
private static IEnumerable GetAllFramesRecursive<T>(IWebElement frame,IWebDriver driver)
{
var result = new Stack<IWebElement>();
BaseIFrame baseClass = new BaseIFrame(driver);
var iFrameList = baseClass._iframes;
foreach (var i in iFrameList)
{
result.Push(i);
foreach (IWebElement e in GetAllFramesRecursive<IWebElement>(i,driver))
{
result.Push(e);
}
}
return result;
}
// Public callable method for unit test
public void GetAlliFramesRecursivePublic<T>(IWebElement test,IWebDriver driver)
{
GetAllFramesRecursive<IWebElement>(test,driver);
}
}
}
BaseIFrameTest
类:
[TestMethod]
public void AssertiFrameLength()
{
Login();
var expected = 0;
BaseIFrame bif = new BaseIFrame(_driver);
bif.GetAlliFramesRecursivePublic<IWebElement>(bif.getFrames()[0],_driver);
var elementSize = _driver.FindElements(By.TagName("*")).Count;
for (var i = 0; i < elementSize; i++)
{
var iframesSize = _driver.FindElements(By.TagName("frame")).Count;
expected += iframesSize;
}
var actual = bif.getFrames();
Assert.AreEqual(expected,actual.Count);
}
解决方法
一棵树的基本的深度优先迭代器看起来像这样:
var stack = new Stack<T>();
stack.Push(rootItem);
while (stack.Count > 0)
{
var current = stack.Pop();
// Process the current item
foreach (var child in current.ChildItems)
{
stack.Push(child);
}
}
如果存在节点多次出现的风险(即,它是图而不是树),则需要某种方法来避免重复访问,例如使用hashSet:
var stack = new Stack<T>();
stack.Push(rootItem);
var visited = new HashSet<T>();
while (stack.Count > 0)
{
var current = stack.Pop();
if (visited.Add(current))
{
// Process the current item
foreach (var child in current.ChildItems)
{
stack.Push(child);
}
}
}
使用迭代而不是递归既避免了stackOverflows的问题,又避免了为树中的每个节点创建新集合的需要。在我的示例中,您可以简单地执行yield return current
作为生成延迟评估的迭代器的过程步骤。将current.ChildItems
替换为生成当前节点的边/子项所需的任何内容。
在您的示例中看起来很奇怪的一件事是,我看不到使用frame
参数的位置。似乎只有driver
用于生成iFrame列表,但是驱动程序在每次调用中似乎都是相同的。