c# - 如何设计我的 C# jQuery API,使其使用起来不会混淆?

标签 c# jquery html-agility-pack interface-design

我是 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 示例?

  1. sq["ul.first"].Find(".foo") ...
    失败:滥用 []属性(property)。

  2. sq.Init("ul.first").Find(".foo") ...
    失败:没有什么能真正迫使程序员从 Init 开始,除非我添加一些奇怪的“初始化”机制;用户可能会尝试从 .Find 开始而没有得到他期望的结果。另外,InitFind无论如何都几乎相同,除了前者也会重置堆栈。

  3. sq.Find("ul.first").Find(".foo") ... .ClearStack()
    缺点:程序员可能会忘记清除堆栈。

  4. 做不到。
    end()未实现。

  5. 使用两个不同的对象。
    也许使用 HtmlDocument作为所有查询应该开始的基础,然后每个方法都返回一个 SharpQuery可以链接的对象。这样HtmlDocument始终保持初始状态,但 SharpQuery对象可能有不同的状态。不幸的是,这意味着我必须两次实现一堆东西(一次用于 HtmlDocument,一次用于 SharpQuery 对象)。

  6. new SharpQuery(sq).Find("ul.first").Find(".foo") ...
    构造函数复制对文档的引用,但重置堆栈。

最佳答案

我认为您在这里遇到的主要障碍是您试图摆脱对每个文档只有一个 SharpQuery 对象的情况。 jQuery 不是这样工作的;一般来说,jQuery 对象是不可变的。当您调用更改元素集的方法时(如 findendadd),它不会更改现有对象,但返回一个新的:

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

这种方法的好处有很多。因为 sqheaderallTheLinks 等都是同一个类,所以每个方法只有一个实现。然而,这些对象中的每一个都引用同一个文档,因此您没有每个节点的多个副本,并且对节点的更改反射(reflect)在该文档上的每个 SharpQuery 对象中(例如,在 allTheLinks 之后.text("foo"), someOfTheLinks.text() == "foo".).

实现 end 和其他基于堆栈的操作也变得容易。当每个方法从另一个方法创建一个新的、过滤的 SharpQuery 对象时,它保留对该父对象的引用(allTheLinksheader headersq)。然后 end 就像返回一个包含与父元素相同的元素的新 SharpQuery 一样简单,例如:

public SharpQuery end()
{
    return new SharpQuery(this.parent.GetAllElements());
}

(或者不管你的语法如何变化。)

我认为这种方法将使您获得最像 jQuery 的行为,并且实现起来相当简单。我肯定会关注这个项目;这是个好主意。

关于c# - 如何设计我的 C# jQuery API,使其使用起来不会混淆?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4092776/

相关文章:

C# - 基于定时器截屏

javascript - 使用clearInterval方法后如何再次运行setInterval函数? - JavaScript

javascript - 更新 : problem showing image while waiting to page to load

c# - 解析 Html 文档获取所有具有 ID 和值的输入字段

c# - 解析 HTML 获取键和值

c# - 使用 JSON 在两个 C# 程序之间传递异常

c# - 如何发布包含 Iformfile 的 View 模型数组的表单?

javascript - maxHeight 的 jQuery 动画

C# htmlagilitypack Node.InnerHTML 大小写不正确,如何正确区分大小写

c# - 如何让游戏玩法忽略 Unity3D 中 UI 按钮的点击?