我是 making a jquery clone对于 C#。现在我已经设置好了,所以每个方法都是 IEnumerable<HtmlNode>
上的扩展方法。所以它适用于已经在使用 HtmlAgilityPack
的现有项目.我以为我可以在不保留状态的情况下逃脱......但是,然后我注意到 jQuery 有两种方法 .andSelf
和 .end
从内部堆栈中“弹出”最近匹配的元素。如果我更改我的类,以便它始终对 SharpQuery 对象而不是枚举对象进行操作,我可以模仿此功能,但仍然存在问题。
使用 JavaScript,您会自动获得 Html 文档,但在使用 C# 时,您必须显式加载它,并且如果需要,您可以使用多个文档。看来,当您调用 $('xxx')
时您实际上是在创建一个新的 jQuery 对象并从一个空堆栈开始。在 C# 中,您不想这样做,因为您不想从 Web 重新加载/重新获取文档。因此,您只需将其加载到 SharpQuery 对象或 HtmlNode 列表中(您只需要 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()
他们必须在他们的行尾打电话?我是否重载了 []
相反,告诉他们从那个开始?我会说“算了,反正没人使用那些状态保留函数吗?”
基本上,您希望如何用 C# 编写 jQuery 示例?
sq["ul.first"].Find(".foo") ...
强>
失败:滥用[]
属性(property)。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
等都是同一个类,所以每个方法只有一个实现。然而,这些对象中的每一个都引用同一个文档,因此您没有每个节点的多个副本,并且对节点的更改反射(reflect)在该文档上的每个 SharpQuery
对象中(例如,在 allTheLinks 之后.text("foo")
, someOfTheLinks.text() == "foo"
.).
实现 end
和其他基于堆栈的操作也变得容易。当每个方法从另一个方法创建一个新的、过滤的 SharpQuery
对象时,它保留对该父对象的引用(allTheLinks
到 header
, header
到 sq
)。然后 end
就像返回一个包含与父元素相同的元素的新 SharpQuery
一样简单,例如:
public SharpQuery end()
{
return new SharpQuery(this.parent.GetAllElements());
}
(或者不管你的语法如何变化。)
我认为这种方法将使您获得最像 jQuery 的行为,并且实现起来相当简单。我肯定会关注这个项目;这是个好主意。
关于c# - 如何设计我的 C# jQuery API,使其使用起来不会混淆?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4092776/