Estoy mirando el componente MvcContrib Grid y estoy fascinado, pero al mismo tiempo rechazado, por un truco sintáctico utilizado en la sintaxis de Grid :
.Attributes(style => "width:100%")
La sintaxis anterior establece el atributo de estilo del HTML generado en width:100%
. Ahora, si prestas atención, 'estilo' no se especifica en ninguna parte. Se deduce del nombre del parámetro en la expresión! Tuve que profundizar en esto y descubrí dónde sucede la 'magia':
Hash(params Func<object, TValue>[] hash)
{
foreach (var func in hash)
{
Add(func.Method.GetParameters()[0].Name, func(null));
}
}
De hecho, el código está usando el tiempo formal de compilación, el nombre de los parámetros para crear el diccionario de pares de nombre-valor de atributo. La construcción de sintaxis resultante es muy expresiva, pero al mismo tiempo muy peligrosa.
El uso general de las expresiones lambda permite el reemplazo de los nombres utilizados sin efectos secundarios. Veo un ejemplo en un libro que dice collection.ForEach(book => Fire.Burn(book))
que sé que puedo escribir en mi código collection.ForEach(log => Fire.Burn(log))
y significa lo mismo . ¡Pero con la sintaxis de MvcContrib Grid aquí de repente, encuentro un código que busca activamente y toma decisiones basadas en los nombres que elijo para mis variables!
Entonces, ¿es esta práctica común con la comunidad C # 3.5 / 4.0 y los amantes de las expresiones lambda? ¿O es un pícaro truco inconformista del que no debería preocuparme?
fuente
Respuestas:
Esto tiene mala interoperabilidad. Por ejemplo, considere este ejemplo de C # - F #
C#:
F#:
Resultado:
Se imprime "arg" (no "yadda").
Como resultado, los diseñadores de bibliotecas deben evitar este tipo de 'abusos' o, al menos, proporcionar una sobrecarga 'estándar' (por ejemplo, que toma el nombre de la cadena como un parámetro adicional) si quieren tener una buena interoperabilidad entre los idiomas .Net.
fuente
Me parece extraño no tanto por el nombre , sino porque la lambda es innecesaria ; podría usar un tipo anónimo y ser más flexible:
Este es un patrón utilizado en gran parte de ASP.NET MVC (por ejemplo), y tiene otros usos (una advertencia , tenga en cuenta también los pensamientos de Ayende si el nombre es un valor mágico en lugar de un llamador específico)
fuente
HtmlAttributes
clase con los atributos esperados (para intellisense) e ignorar aquellos connull
valores ...Solo quería agregar mi opinión (soy el autor del componente de cuadrícula MvcContrib).
Esto definitivamente es abuso de lenguaje, sin duda. Sin embargo, realmente no lo consideraría contrario a la intuición: cuando miras una llamada
Attributes(style => "width:100%", @class => "foo")
, creo que es bastante obvio lo que está sucediendo (ciertamente no es peor que el enfoque de tipo anónimo). Desde una perspectiva inteligente, estoy de acuerdo en que es bastante opaco.
Para aquellos interesados, alguna información de fondo sobre su uso en MvcContrib ...
Agregué esto a la cuadrícula como una preferencia personal: no me gusta el uso de tipos anónimos como diccionarios (tener un parámetro que toma "objeto" es tan opaco como uno que toma los parámetros Func []) y el inicializador de la colección Diccionario es más bien detallado (tampoco soy un fanático de las interfaces fluidas detalladas, por ejemplo, tener que encadenar varias llamadas a un atributo ("estilo", "display: none"). Atributo ("clase", "foo"), etc.)
Si C # tuviera una sintaxis menos detallada para los literales del diccionario, entonces no me habría molestado en incluir esta sintaxis en el componente de cuadrícula :)
También quiero señalar que el uso de esto en MvcContrib es completamente opcional: estos son métodos de extensión que envuelven sobrecargas que toman un IDictionary en su lugar. Creo que es importante que si proporciona un método como este, también debe admitir un enfoque más "normal", por ejemplo, para interoperabilidad con otros idiomas.
Además, alguien mencionó la 'sobrecarga de reflexión' y solo quería señalar que realmente no hay mucha sobrecarga con este enfoque: no hay una compilación de reflexión o expresión de tiempo de ejecución involucrada (ver http://blog.bittercoder.com /PermaLink,guid,206e64d1-29ae-4362-874b-83f5b103727f.aspx ).
fuente
Yo preferiría
Es mucho más explícito y estándar y no se gana nada usando lambdas.
fuente
html.Attributes.Add("style", "width:100%");
no se lee tan bien comostyle = "width:100%"
(el html real generado), mientras questyle => "width:100%"
está muy cerca de cómo se ve en el HTML resultante.Bienvenido a Rails Land :)
Realmente no tiene nada de malo, siempre y cuando sepas lo que está sucediendo. (Es cuando este tipo de cosas no está bien documentado que hay un problema).
La totalidad del marco Rails se basa en la idea de la convención sobre la configuración. Nombrar las cosas de una determinada manera te lleva a una convención que están usando y obtienes una gran cantidad de funciones de forma gratuita. Seguir la convención de nombres te lleva a donde vas más rápido. Todo funciona de manera brillante.
Otro lugar donde he visto un truco como este es en las aserciones de llamadas a métodos en Moq. Se pasa una lambda, pero la lambda nunca se ejecuta. Solo usan la expresión para asegurarse de que se realizó la llamada al método y lanzan una excepción si no.
fuente
Esto es horrible en más de un nivel. Y no, esto no se parece en nada a Ruby. Es un abuso de C # y .NET.
Ha habido muchas sugerencias sobre cómo hacer esto de una manera más directa: tuplas, tipos anónimos, una interfaz fluida, etc.
Lo que lo hace tan malo es que es solo una manera de imaginarse por su propio bien:
¿Qué sucede cuando necesita llamar a esto desde Visual Basic?
.Attributes(Function(style) "width:100%")
Es completamente contrario a la intuición, e intellisense proporcionará poca ayuda para descubrir cómo pasar cosas.
Es innecesariamente ineficiente.
Nadie tendrá idea de cómo mantenerlo.
¿Cuál es el tipo de argumento que va a los atributos? ¿es
Func<object,string>
? ¿Cómo se revela esa intención? ¿Qué va a decir su documentación de Intellisense, "Por favor ignore todos los valores del objeto"?Creo que está completamente justificado tener esos sentimientos de repulsión.
fuente
Estoy en el campo de "brillo de sintaxis", si lo documentan claramente, y se ve increíblemente genial, ¡casi no hay problema con eso!
fuente
Ambos. Es abuso de expresiones lambda Y brillo de sintaxis.
fuente
Casi nunca me encontré con este tipo de uso. Creo que es "inapropiado" :)
Esta no es una forma común de uso, es inconsistente con las convenciones generales. Este tipo de sintaxis tiene ventajas y desventajas, por supuesto:
Contras
Pros
En pocas palabras : en el diseño de API pública, habría elegido una forma más explícita.
fuente
No, ciertamente no es una práctica común. Es contrario a la intuición, no hay forma de mirar el código para descubrir qué hace. Tienes que saber cómo se usa para entender cómo se usa.
En lugar de suministrar atributos utilizando una matriz de delegados, los métodos de encadenamiento serían más claros y funcionarían mejor:
Aunque esto es un poco más para escribir, es claro e intuitivo.
fuente
.Attribute("style", "width:100%")
me dastyle="width:100%"
, pero por lo que sé, podría darmefoooooo
. No estoy viendo la diferencia.¿Puedo usar esto para acuñar una frase?
magic lambda (n): una función lambda utilizada únicamente con el propósito de reemplazar una cadena mágica.
fuente
Qué hay de malo con lo siguiente:
fuente
Todo este despotricar sobre la "horrible" es un montón de muchachos de C # que reaccionan de forma exagerada (y yo soy un programador de C # desde hace mucho tiempo y sigo siendo un gran admirador del lenguaje). No hay nada horrible en esta sintaxis. Es simplemente un intento de hacer que la sintaxis se parezca más a lo que está tratando de expresar. Cuanto menos "ruido" haya en la sintaxis de algo, más fácil será entenderlo el programador. Disminuir el ruido en una línea de código solo ayuda un poco, pero deja que eso se acumule en más y más código, y resulta ser un beneficio sustancial.
Este es un intento del autor de luchar por los mismos beneficios que los DSL le brindan: cuando el código "parece" lo que está tratando de decir, ha llegado a un lugar mágico. Puede debatir si esto es bueno para la interoperabilidad, o si es más agradable que los métodos anónimos para justificar parte del costo de "complejidad". Es justo ... así que en su proyecto debe tomar la decisión correcta de usar este tipo de sintaxis. Pero aún así ... este es un intento inteligente por parte de un programador de hacer lo que, al final del día, todos intentamos hacer (ya sea que nos demos cuenta o no). Y lo que todos intentamos hacer es esto: "Dígale a la computadora lo que queremos que haga en un lenguaje lo más cercano posible a cómo pensamos sobre lo que queremos que haga".
Acercarse a expresar nuestras instrucciones a las computadoras de la misma manera que pensamos internamente es una clave para hacer que el software sea más fácil de mantener y más preciso.
EDITAR: Dije "la clave para hacer que el software sea más fácil de mantener y más preciso", que es un poco exageradamente ingenuamente exagerado de unicornio. Lo cambié a "una clave".
fuente
Este es uno de los beneficios de los árboles de expresión: se puede examinar el código en sí para obtener información adicional. Así es como
.Where(e => e.Name == "Jamie")
se puede convertir en la cláusula SQL Where equivalente. Este es un uso inteligente de los árboles de expresión, aunque espero que no vaya más allá de esto. Es probable que cualquier cosa más compleja sea más difícil que el código que espera reemplazar, por lo que sospecho que será autolimitado.fuente
Es un enfoque interesante. Si restringió el lado derecho de la expresión para que sean constantes solo entonces podría implementar usando
Lo que creo que es lo que realmente quieres en lugar del delegado (estás usando la lambda para obtener nombres de ambos lados). Ver implementación ingenua a continuación:
Esto incluso podría abordar la preocupación de interoperabilidad entre idiomas que se mencionó anteriormente en el hilo.
fuente
El código es muy inteligente, pero potencialmente causa más problemas que resuelve.
Como ha señalado, ahora hay una oscura dependencia entre el nombre del parámetro (estilo) y un atributo HTML. No se realiza la comprobación del tiempo de compilación. Si el nombre del parámetro está mal escrito, la página probablemente no tendrá un mensaje de error de tiempo de ejecución, pero será mucho más difícil de encontrar un error lógico (sin error, pero con un comportamiento incorrecto).
Una mejor solución sería tener un miembro de datos que se pueda verificar en tiempo de compilación. Entonces, en lugar de esto:
El compilador puede verificar el código con una propiedad Style:
o incluso:
Eso es más trabajo para los autores del código, pero este enfoque aprovecha la fuerte capacidad de verificación de tipos de C #, que ayuda a evitar que los errores entren en el código en primer lugar.
fuente
de hecho, parece Ruby =), al menos para mí el uso de un recurso estático para una "búsqueda" dinámica posterior no se ajusta a las consideraciones de diseño de la API, espero que este truco inteligente sea opcional en esa API.
Podríamos heredar de IDictionary (o no) y proporcionar un indexador que se comporte como una matriz php cuando no necesite agregar una clave para establecer un valor. Será un uso válido de la semántica de .net, no solo c #, y aún necesita documentación.
espero que esto ayude
fuente
En mi opinión es abuso de las lambdas.
En cuanto al brillo de sintaxis, me resulta
style=>"width:100%"
confuso. Particularmente por el=>
lugar de=
fuente
En mi humilde opinión, es una forma genial de hacerlo. A todos nos encanta el hecho de que nombrar un controlador de clase lo convertirá en un controlador en MVC, ¿verdad? Entonces, hay casos en los que el nombre sí importa.
También la intención es muy clara aquí. Es muy fácil de entender que
.Attribute( book => "something")
resultarábook="something"
y.Attribute( log => "something")
resultará enlog="something"
Supongo que no debería ser un problema si lo tratas como una convención. Soy de la opinión de que lo que sea que te haga escribir menos código y haga que la intención sea obvia es algo bueno.
fuente
Si los nombres de los métodos (func) se eligen bien, esta es una manera brillante de evitar dolores de cabeza por mantenimiento (es decir: agregar un nuevo func, pero olvidó agregarlo a la lista de asignación de parámetros de función). Por supuesto, debe documentarlo en gran medida y será mejor que genere automáticamente la documentación de los parámetros a partir de la documentación para las funciones de esa clase ...
fuente
Creo que esto no es mejor que las "cuerdas mágicas". Tampoco soy muy fanático de los tipos anónimos para esto. Necesita un enfoque mejor y fuertemente tipado.
fuente