Sono making a jquery clone per C#. In questo momento ho impostato in modo che ogni metodo sia un metodo di estensione su IEnumerable<HtmlNode>
, quindi funziona bene con i progetti esistenti che già utilizzano HtmlAgilityPack
. Pensavo di poter andare via senza preservare lo stato ... tuttavia, ho notato che jQuery ha due metodi .andSelf
e .end
che "pop" gli elementi abbinati più di recente da uno stack interno. Posso imitare questa funzionalità se cambio la mia classe in modo che funzioni sempre sugli oggetti SharpQuery invece che sugli enumerabili, ma c'è ancora un problema.Come progettare la mia API jQuery C# in modo che non sia di confusione da usare?
Con JavaScript, viene fornito automaticamente il documento Html, ma quando si lavora in C# è necessario caricarlo in modo esplicito e, se si desidera, è possibile utilizzare più di un documento. Sembra che quando chiami lo $('xxx')
stai essenzialmente creando un nuovo oggetto jQuery e ricominci da zero con uno stack vuoto. In C#, non vorresti farlo, perché non vuoi ricaricare/rimettere il documento dal web. Quindi, invece, lo si carica una volta in un oggetto SharpQuery o in un elenco di HtmlNode (è necessario solo il DocumentNode per iniziare).
nella documentazione jQuery, danno questo esempio
$('ul.first').find('.foo')
.css('background-color', 'red')
.end().find('.bar')
.css('background-color', 'green')
.end();
Non ho un metodo di inizializzazione perché non posso sovraccaricare l'operatore ()
, quindi basta iniziare con sq.Find()
invece, che opera sul radice del documento, essenzialmente facendo la stessa cosa. Ma poi le persone cercheranno di scrivere sq.Find()
su una riga, e poi su sq.Find()
da qualche parte lungo la strada, e (giustamente) si aspettano che funzioni nuovamente nella root del documento ... ma se sto mantenendo lo stato, allora tu Ho appena modificato il contesto dopo la prima chiamata.
Allora ... come dovrei progettare il mio API? Aggiungo un altro metodo Init
che dovrebbe iniziare con tutte le query che azzerano lo stack (ma come faccio a forzare l'avvio con quello?) O aggiungere uno Reset()
che devono chiamare alla fine della loro riga? Sovraccarico il []
e dico loro di iniziare con quello? Devo dire "dimenticalo, nessuno usa quelle funzioni conservate dallo stato comunque?"
In sostanza, come ti piacerebbe che l'esempio di jQuery fosse scritto in C#?
sq["ul.first"].Find(".foo") ...
cadute: abusi della proprietà[]
.sq.Init("ul.first").Find(".foo") ...
cadute: Nulla obbliga davvero il programmatore di iniziare con Init, a meno che non aggiungo qualche strano meccanismo "inizializzato"; l'utente potrebbe provare a iniziare con.Find
e non ottenere il risultato che si aspettava. Inoltre,Init
eFind
sono praticamente uguali in ogni caso, tranne che il precedente reimposta anche lo stack.sq.Find("ul.first").Find(".foo") ... .ClearStack()
cadute: programmatore può dimenticare di cancellare lo stack.Impossibile farlo.
end()
non implementato.Utilizzare due oggetti diversi.
È possibile utilizzareHtmlDocument
come base da cui iniziare tutte le query, quindi ogni metodo restituisce un oggettoSharpQuery
che può essere concatenato. In questo modo loHtmlDocument
mantiene sempre lo stato iniziale, ma gli oggettiSharpQuery
potrebbero avere stati diversi. Questo purtroppo significa che devo implementare un paio di cose due volte (una volta per HtmlDocument, una volta per l'oggetto SharpQuery).new SharpQuery(sq).Find("ul.first").Find(".foo") ...
Le copie costruttore un riferimento al documento, ma ripristina la pila.
IMO 5) è l'opzione migliore. È certamente leggibile e intuitivo che le query inizino da "HtmlDocument" e, come per il lavoro duplicato, si spera che sia possibile implementarlo in modo che solo le firme dei metodi siano duplicate, ma non la logica effettiva. (Concatenare le chiamate ad alcune classi comuni che fanno il lavoro.) Mi piace anche l'opzione 1) e personalmente non la vedo come un "abuso" delle parentesi. :) –
@Kirk: '[]' indica generalmente un'operazione 'O (1)' ... come se i dati fossero già indicizzati. In questo caso, in realtà deve effettuare una ricerca completa. – mpen
Tuttavia, mi sto appoggiando verso (1) ora. In realtà, ho iniziato a codificarlo in questo modo. Ciò impedisce agli utenti di saltare direttamente con '.Find()' perché il contesto non è impostato fino a quando non chiamano '[]'. Ad esempio, '.Find()' semplicemente non troverà nulla perché non ha nulla da cercare. '[]' ripristina il contesto sul nodo del documento e quindi può iniziare la ricerca. – mpen