Je suis making a jquery clone pour C#. En ce moment, je l'ai mis en place de sorte que chaque méthode est une méthode d'extension sur IEnumerable<HtmlNode>
de sorte qu'il fonctionne bien avec les projets existants qui utilisent déjà HtmlAgilityPack
. Je pensais pouvoir m'enfuir sans préserver l'état ... cependant, j'ai remarqué que jQuery a deux méthodes .andSelf
et .end
qui "pop" les éléments les plus récemment appariés d'une pile interne. Je peux imiter cette fonctionnalité si je change ma classe afin qu'elle fonctionne toujours sur les objets SharpQuery au lieu des énumérables, mais il y a toujours un problème.Comment concevoir mon API C# jQuery de telle sorte qu'elle ne prête pas à confusion?
Avec JavaScript, le document Html vous est automatiquement fourni, mais lorsque vous travaillez en C#, vous devez le charger explicitement, et vous pouvez utiliser plus d'un document si vous le souhaitez. Il semble que lorsque vous appelez $('xxx')
, vous créez essentiellement un nouvel objet jQuery et commencez avec une pile vide. En C#, vous ne voudriez pas faire cela, parce que vous ne voulez pas recharger/recharger le document sur le web. Donc, à la place, vous le chargez une fois dans un objet SharpQuery ou dans une liste de HtmlNodes (vous avez juste besoin du DocumentNode pour commencer).
Dans les docs jQuery, ils donnent cet exemple
$('ul.first').find('.foo')
.css('background-color', 'red')
.end().find('.bar')
.css('background-color', 'green')
.end();
Je n'ai pas une méthode d'initialisation parce que je ne peux pas surcharger l'opérateur ()
, de sorte que vous venez de commencer avec sq.Find()
à la place, qui fonctionne sur la racine du document, essentiellement faire la même chose. Mais alors les gens vont essayer d'écrire sq.Find()
sur une ligne, puis sq.Find()
quelque part sur la route, et (légitimement) s'attendre à ce qu'il fonctionne à nouveau sur la racine du document ... mais si je suis en état, alors vous J'ai juste modifié le contexte après le premier appel.
Alors ... comment devrais-je concevoir mon API? Dois-je ajouter une autre méthode Init
que toutes les requêtes devraient commencer avec qui réinitialise la pile (mais alors comment puis-je les forcer à commencer par cela?), Ou ajouter un Reset()
qu'ils doivent appeler à la fin de leur ligne? Dois-je surcharger le []
à la place et leur dire de commencer avec ça? Est-ce que je dis "oubliez-le, personne n'utilise ces fonctions préservées par l'état de toute façon?"
Fondamentalement, comment voulez-vous que cet exemple jQuery soit écrit en C#?
sq["ul.first"].Find(".foo") ...
écroulements: la propriété[]
Exactions.sq.Init("ul.first").Find(".foo") ...
écroulements: Rien oblige vraiment le programmeur de commencer par Init, à moins que j'ajouter un mécanisme bizarre « initialisé »; l'utilisateur peut essayer de commencer avec.Find
et ne pas obtenir le résultat qu'il attendait. En outre,Init
etFind
sont à peu près identiques de toute façon, sauf que le premier réinitialise la pile aussi.sq.Find("ul.first").Find(".foo") ... .ClearStack()
écroulements: programmeur peut oublier de dégager la pile.Impossible de le faire.
end()
pas implémenté.Utilisez deux objets différents.
Peut-être utiliserHtmlDocument
comme base que toutes les requêtes devraient commencer, puis chaque méthode retourne ensuite un objetSharpQuery
qui peut être chaîné. De cette façon, leHtmlDocument
conserve toujours l'état initial, mais les objetsSharpQuery
peuvent avoir des états différents. Cela signifie malheureusement que je dois implémenter un tas de choses deux fois (une fois pour HtmlDocument, une fois pour l'objet SharpQuery).new SharpQuery(sq).Find("ul.first").Find(".foo") ...
Les copies du constructeur une référence au document, mais remet à zéro la pile.
IMO 5) est la meilleure option. Il est certainement lisible et intuitif pour les requêtes de commencer à partir de 'HtmlDocument', et quant au travail en double - espérons que vous pouvez l'implémenter afin que seules les signatures de méthodes soient dupliquées, mais pas la logique réelle. (Enchaînant les appels à une classe commune qui fait le travail.) J'aime aussi l'option 1) et personnellement ne la vois pas comme un «abus» des parenthèses. :) –
@Kirk: '[]' indique généralement une opération 'O (1)' ... comme si les données étaient déjà indexées. Dans ce cas, il doit faire une recherche complète. – mpen
Néanmoins, je penche vers (1) maintenant. En fait, j'ai commencé à le coder de cette façon. Cela empêche les utilisateurs de sauter directement avec '.Find()' car le contexte n'est pas défini avant d'appeler '[]'. c'est-à-dire que '.Find()' ne trouve rien parce qu'il n'a rien à chercher. '[]' réinitialise le contexte sur le noeud de document, puis il peut commencer à chercher. – mpen