Pregunta

hacer un clon de jQuery para C #. En este momento lo tengo configurado para que cada método es un método de extensión en IEnumerable<HtmlNode> por lo que funciona bien con los proyectos existentes que ya están utilizando HtmlAgilityPack. Pensé que podría salir de allí sin preservar el estado ... sin embargo, entonces me di cuenta de jQuery tiene dos métodos .andSelf y .end la que "pop" los elementos más recientemente emparejados fuera una pila interna. Puedo imitar esta funcionalidad si cambio de clase de manera que siempre opera sobre objetos SharpQuery en lugar de enumerables, pero todavía hay un problema.

Con Javascript, que está dado el documento HTML de forma automática, pero cuando se trabaja en C # tiene que cargar de forma explícita, y se puede utilizar más de un documento si querías. Parece que cuando se llama a $('xxx') que está esencialmente creando un nuevo objeto jQuery y empezar de nuevo con una pila vacía. En C #, que no le gustaría hacer eso, ya que no desea volver a cargar / refetch el documento de la web. Así que en lugar, se carga una vez, ya sea en un objeto SharpQuery, o en una lista de HtmlNodes (sólo tiene la DocumentNode para empezar).

En los docs jQuery, dan este ejemplo

$('ul.first').find('.foo')
  .css('background-color', 'red')
.end().find('.bar')
  .css('background-color', 'green')
.end();

No tengo un método de inicialización porque no puedo sobrecargar el operador (), por lo que acaba de empezar con sq.Find() lugar, que opera en la raíz del documento, haciendo esencialmente la misma cosa. Pero la gente luego van a tratar de sq.Find() escribir en una sola línea, y en algún lugar y luego sq.Find() en el camino, y (con razón) esperan que operan en la raíz del documento de nuevo ... pero si estoy mantener el estado, entonces han modificado el contexto simplemente después de la primera llamada.

Así que ... ¿cómo debo diseñar mi API? Agrego otro método Init que todas las consultas deben comenzar con que restablece la pila (pero entonces ¿cómo puedo obligarlos a empezar con eso?), O añadir un Reset() que tienen que llamar al final de su línea? Puedo sobrecargar el [] lugar y decirles a empezar con eso? Por qué digo "olvidarse de él, nadie usa esas funciones conservada por el estado de todos modos?"

Básicamente, ¿cómo le gustaría que el ejemplo jQuery para ser escrito en C #?

  1. sq["ul.first"].Find(".foo") ...
    Desventajas:. Abusa de la propiedad []

  2. sq.Init("ul.first").Find(".foo") ...
    Desventajas: En realidad, nada obliga al programador para comenzar con Init, a menos que añado algún mecanismo raro "inicializado"; usuario podría intentar el inicio de la .Find y no conseguir el resultado que esperaba. Además, Init y Find son de todos modos más o menos idénticos, excepto los antiguos restablece la pila también.

  3. sq.Find("ul.first").Find(".foo") ... .ClearStack()
    Desventajas:. Programador puede olvidarse de limpiar la pila

  4. No puede hacerlo.
    end() no se han aplicado.

  5. Usar dos objetos diferentes.
    Tal vez utilizar HtmlDocument como la base de que todas las consultas deben empezar, y luego cada método devuelve un objeto a partir de entonces SharpQuery que puede ser de cadena. De esta forma el HtmlDocument siempre mantiene el estado inicial, pero los objetos SharpQuery puede tener diferentes estados. Esto, lamentablemente, significa que tengo que poner en práctica un montón de cosas dos veces (una vez para HtmlDocument, una vez para el objeto SharpQuery).

  6. new SharpQuery(sq).Find("ul.first").Find(".foo") ...
    Las copias del constructor una referencia al documento, pero se restablece la pila.

¿Fue útil?

Solución

Creo que el principal obstáculo que se está ejecutando en este caso es que usted está tratando de salirse con sólo tener un objeto SharpQuery para cada documento. Así no es como funciona jQuery; en general, los objetos jQuery son inmutables. Cuando se llama a un método que cambia el conjunto de elementos (como find o end o add), que no altera el objeto existente, pero devuelve una nueva:

var theBody = $('body');
// $('body')[0] is the <body>
theBody.find('div').text('This is a div');
// $('body')[0] is still the <body>

(véase el documentación de end para obtener más información)

SharpQuery debe funcionar de la misma manera. Una vez que se crea un objeto SharpQuery con un documento, las llamadas de método debe devolver los objetos nuevos SharpQuery, hace referencia a un conjunto diferente de elementos de un mismo documento. Por ejemplo:

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

Los beneficios de este enfoque son varias. Debido sq, header, allTheLinks, etc, todos ellos de la misma clase, que sólo tiene una aplicación de cada método. Sin embargo, cada uno de estos objetos referencias del mismo documento, por lo que no tiene varias copias de cada nodo, y los cambios en los nodos se reflejan en cada objeto SharpQuery en ese documento (por ejemplo, después allTheLinks.text("foo"), someOfTheLinks.text() == "foo".).

Ejecución end y las otras manipulaciones basados ??en la pila también se hace fácil. Como cada método crea un nuevo objeto SharpQuery filtrada de otro, conserva una referencia a ese objeto padre (allTheLinks a header, header a sq). Entonces end es tan simple como el retorno de una nueva SharpQuery que contiene los mismos elementos que los padres, como:

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

(o como su sintaxis sacude.)

Creo que este enfoque le dará el más jQuery-como el comportamiento, con una aplicación bastante fácil. Definitivamente, voy a ser de mantenimiento de un ojo en este proyecto; que es una gran idea.

Otros consejos

I se inclinaría hacia una variante de la opción 2. En jQuery $ () es una llamada a la función. C # no tiene funciones globales, una llamada a la función estática es el más cercano. Me gustaría utilizar un método que indica que usted está creando un envoltorio como ..

SharpQuery.Create("ul.first").Find(".foo")

Yo no estaría preocupado por el acortamiento de SharpQuery cuadrados ya que los usuarios medios de IntelliSense no tendrán que escribir todo el asunto (y si tienen ReSharper sólo tienen que escribir todos modos SQ).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top