我是C的making a jquery clone。现在我已经把它设置好了,所以每种方法都是在IEnumerable<HtmlNode>
上的一个扩展方法,所以它与已经使用HtmlAgilityPack
的现有项目很好地工作。我以为我可以逃走而不必保护国家…然而,我注意到jquery有两个方法.andSelf
和.end
从内部堆栈中“弹出”最近匹配的元素。如果我更改类,使其始终在sharpquery对象(而不是可枚举对象)上运行,我可以模拟此功能,但仍然存在问题。
使用javascript,可以自动获得html文档,但在c中工作时,必须显式加载它,如果需要,可以使用多个文档。当您调用$('xxx')
时,实际上是在创建一个新的jquery对象,并从一个空堆栈开始刷新。在C_中,您不想这样做,因为您不想从Web重新加载/重新提取文档。因此,您可以将其加载到sharpquery对象或htmlnodes列表中(只需要documentnode开始)。
在jquery文档中,他们给出了这个示例
$('ul.first').find('.foo')
.css('background-color', 'red')
.end().find('.bar')
.css('background-color', 'green')
.end();
我没有初始化器方法,因为我不能重载
()
运算符,所以您只需从sq.Find()
开始,它在文档的根上操作,基本上做相同的事情。但接下来人们会尝试在一行写上sq.Find()
,然后在某个地方写下sq.Find()
,并(正确地)期望它再次在文档的根目录上运行……但是如果我在维护状态,那么在第一次调用之后,您刚刚修改了上下文。所以…我应该如何设计我的api?我是否添加了另一个
Init
方法,所有查询都应以该方法开头,该方法重置堆栈(但如何强制它们以该方法开头?),或者在行尾添加一个他们必须调用的Reset()
?我是否应该让[]
超载并让他们从这个开始?我是不是说“算了,反正没人用那些状态保持的函数?”基本上,您希望jquery示例如何用c编写?
sq["ul.first"].Find(".foo") ...
失败:滥用
[]
属性。sq.Init("ul.first").Find(".foo") ...
缺点:没有什么能真正迫使程序员从init开始,除非我添加了一些奇怪的“初始化”机制;用户可能尝试从
.Find
开始,但没有得到他期望的结果。而且,Init
和Find
几乎是一样的,除了前者也会重置堆栈。sq.Find("ul.first").Find(".foo") ... .ClearStack()
失败:程序员可能会忘记清除堆栈。
做不到。
end()
未实现。使用两个不同的对象。
也许使用
HtmlDocument
作为所有查询的基础,然后每个方法都返回一个可以链接的SharpQuery
对象。这样,HtmlDocument
始终保持初始状态,但是SharpQuery
对象可能有不同的状态。不幸的是,这意味着我必须两次实现一堆东西(一次用于htmldocument,一次用于sharpquery对象)。new SharpQuery(sq).Find("ul.first").Find(".foo") ...
构造函数复制对文档的引用,但重置堆栈。
最佳答案
我认为您在这里遇到的主要障碍是,您试图摆脱每个文档只有一个SharpQuery
对象的困扰。jquery不是这样工作的;一般来说,jquery对象是不可变的。当调用一个改变元素集合的方法(如find
或end
或add
)时,它不会改变现有对象,而是返回一个新对象:
var theBody = $('body');
// $('body')[0] is the <body>
theBody.find('div').text('This is a div');
// $('body')[0] is still the <body>
(更多信息请参见documentation of
end
)sharpquery应该以同样的方式操作。使用文档创建sharpquery对象后,方法调用应返回新的
SharpQuery
对象,引用同一文档的不同元素集。例如:var sq = SharpQuery.Load(new Uri("http://api.jquery.com/category/selectors/"));
var header = sq.Find("h1"); // doesn't change sq
var allTheLinks = sq.Find(".title-link") // all .title-link in the whole document; also doesn't change sq
var someOfTheLinks = header.Find(".title-link"); // just the .title-link in the <h1>; again, doesn't change sq or header
这种方法有几个好处。因为
sq
、header
、allTheLinks
等都是同一个类,所以每个方法只有一个实现。然而,这些对象中的每个都引用同一个文档,因此您没有每个节点的多个副本,对节点的更改反映在该文档上的每个SharpQuery
对象中(例如,在allTheLinks.text("foo")
,someOfTheLinks.text() == "foo"
之后)。实现
end
和其他基于堆栈的操作也变得很容易。当每个方法从另一个方法创建新的过滤对象时,它将保留对父对象的引用(SharpQuery
到allTheLinks
,header
到header
)。那么sq
就像返回一个包含与父元素相同元素的新end
一样简单,比如:public SharpQuery end()
{
return new SharpQuery(this.parent.GetAllElements());
}
(或者不管你的语法如何变化。)
我认为这种方法将使您获得最类似jquery的行为,并且实现起来相当简单。我一定会关注这个项目的,这是个好主意。