2009-12-16 10 views
15

Un compañero de trabajo mío se le ocurrió esto y me pregunto qué piensan los demás? Personalmente, me parece interesante, pero me pregunto si es una gran partida. Ejemplos de código a continuación. Métodos de extensión en la parte inferior.Más fluido C#/.NET

Pensamientos generales por favor. ¿Otros métodos de extensión que podrían agregarse?

var ddl = Page.FindControl("LocationDropDownList") as DropDownList; 

ddl.Visible = true; 
ddl.SelectedValue = "123"; 

if(isAdmin) 
    ddl .SelectedValue = "111"; 

se convierte en:

Page.FindControl("LocationDropDownList") 
    .CastAs<DropDownList>() 
    .With(d => d.Visible = true) 
    .With(d => d.SelectedValue = "123") 
    .WithIf(isAdmin, d => d.Items.Add(new ListItem("Admin", "1"))); 

O:

Page.FindControl("LocationDropDownList") 
     .CastAs<DropDownList>() 
     .With(d => 
     { 
      d.Visible = true; 
      d.SelectedValue = "123"; 
     }) 
     .WithIf(isAdmin, d => d.SelectedValue = "111"); 

Los métodos de extensión:

public static TResult CastAs<TResult>(this object obj) where TResult : class 
{ 
    return obj as TResult; 
} 

public static T With<T>(this T t, Action<T> action) 
{ 
    if (action == null) 
     throw new ArgumentNullException("action"); 

    action(t); 

    return t; 
} 

public static T WithIf<T>(this T t, bool condition, Action<T> action) 
{ 
    if (action == null) 
     throw new ArgumentNullException("action"); 

    if (condition) 
     action(t); 

    return t; 
} 
+19

** Cada diseño debe resolver un problema. ** ¿Qué problema es este diseño API fluida tratando de resolver? Parece agregar complejidad sin crear valor. – LBushkin

+2

¡Esto es como tratar de matar una mosca con una bazooka! No hay nada que ganar, solo más líneas de código. – JOBG

+0

En mi opinión, 'WithIf' es mucho menos legible que una simple declaración if. Si va a hacer una interfaz "fluida", ¡al menos hágala leer con fluidez! –

Respuesta

59

Entre mis reglas generales para escribir código claro está: poner todos los efectos secundarios en las declaraciones; las expresiones de no declaración no deberían tener efectos secundarios.

Su primera versión del programa sigue claramente esta regla. La segunda versión claramente lo viola.

Una idea adicional: si tuviera que leer código como el código que ha mostrado, naturalmente asumiría que el propósito del código era construir una estructura evaluada perezosamente que representara esas operaciones - esto es exactamente por qué las comprensiones de consulta en C# 3 se construyen de esta manera. El resultado de la expresión de consulta es un objeto que representa la aplicación diferida de la consulta.

Si su intención es captar la noción de "ejecutar estos efectos secundarios de manera diferida en un momento posterior de mi elección", entonces este es un enfoque sensato. Esencialmente, lo que estás construyendo es una mónada de efecto secundario. Si su intención es simplemente proporcionar una sintaxis diferente para el código ejecutado con impaciencia, entonces esto es simplemente confuso, prolijo e innecesario.

+1

+1 para una excelente guía. – LBushkin

+3

Otra guía para casos como este en mi humilde opinión, es que: ** cada diseño debería resolver un problema **. No está claro qué problema está tratando de resolver la API con fluidez aquí. – LBushkin

+1

Parece que el experto ha hablado. – andleer

49

veo ninguna ventaja a este que además de ser confuso para el lector. Con respecto a mi contendiente, me gustaría saber en qué planeta es más legible. Por lo que puedo decir, la primera versión tiene una legibilidad más o menos perfecta, mientras que es bastante legible, pero hace que el lector se pregunte si hay alguna magia extraña dentro de With y WithIf.

En comparación con la primera versión, es más larga, más difícil de escribir, menos obvia y de menor rendimiento.

+9

+1, absolutamente ninguna ganancia, y ahora quien lea el código tendrá que buscar 'WithIf' (horrible nombre, por cierto) y amigos para entenderlo. –

+1

@mquander, no estoy de acuerdo, es solo la palabra "Con" lo que está causando la confusión. Si fuera "Hacer", sería explícitamente obvio. – grenade

+0

@grenate: incluso con Do, la parte 'Do (d =>)' parece terriblemente redundante. – peterchen

4

¡Esto es un abuso de método de extensión si alguna vez lo vi!

11

Supongo que no puedo ver lo que las nuevas versiones te dan. El original es bastante claro y es menos prolijo. Yo diría que sería más rápido también. Evitaría usar (¿abusar?) Funciones de lenguaje como esta a menos que haya un claro beneficio.

4

Son solo diferentes estilos de codificación, ¿qué quiere decir con "partida demasiado grande"? ¿Salida de qué? De lo que estás acostumbrado? Solo tú puedes decidir eso. Diré que el bloque With de VB ha hecho más daño que bien a la legibilidad del código, y no trataría de replicar el comportamiento en C#, pero eso es solo mi preferencia.

que casi siempre lo uso para FindControl (sí, inflexible a RepeaterItem, no tiene por qué ser, pero esa es la única cosa que siempre lo uso para todos modos):

public static T FindControl<T>(this RepeaterItem item, string id) 
{ 
    return item.FindControl(id) as T; 
} 

e invocar que de este modo:

Literal myLiteral = e.Item.FindControl<Literal>("myLiteral"); 
+0

Supongo que una desviación de los estilos de codificación de C# más comunes. Sin duda, una desviación de los estándares de codificación de mi organización. – andleer

+0

Mientras aún diría que esto es sobre todo hasta su preferencia, voy a añadir que si su organización ha estándares de codificación, lo ideal es que no apartarse de ellas en absoluto * * =) –

2

Mis 2 centavos: se ve bien, mi único comentario es que "con el" tipo de implica algo así como "¿Dónde" o "que tiene" cuando se está configurando en realidad una propiedad. Sugeriría un nombre de método de algo como "Hacer", "Ejecutar" o "Establecer", pero quizás sea solo mi extraña visión del mundo.

¿Qué tal:

Page.WithControl<DropDownList>("LocationDropDownList") 
    .Do(d => d.Visible = true) 
    .Do(d => d.SelectedValue = "123") 
    .DoIf(isAdmin, d => d.Items.Add(new ListItem("Admin", "1"))); 
+0

Con() podría utilizarse para extraer o "obtener" valores también. .Con (d => Fred = D.TEXT) – andleer

+0

Sólo quiero decir que "Con" (para mí) que hace que parezca que está comprobando para un valor, por ejemplo: .Con (d => D.TEXT == "Foo ") en lugar de ejecutar una acción, por ej.Con (d => DoStuff (d)) podría ser: .Execute (d => DoStuff (d)) o .No (d => DoStuff (d)) – grenade

+0

De acuerdo. Let() es una opción mejor que With() – andleer

0

digo palo con la primera versión sin los métodos de extensión o expresiones lamba. Estos son conceptos relativamente nuevos, por lo que no muchos desarrolladores tendrán un control sobre ellos fuera de su uso en la recuperación/manipulación de datos de una base de datos. Si los usa puede tener un golpe en el costo de mantenimiento. Es agradable decir "lee si esto es griego para ti"; pero en la vida real ese puede ser el mejor enfoque.

+2

Esta no es una razón para detener la inovación. Es una razón para ayudar a enseñar a sus compañeros y avanzar en la innovación. –

+4

No es práctico ni prudente abstenerse de hacer lo correcto porque otras personas podrían no tener un conocimiento práctico apropiado del idioma. La pregunta en cuestión debería ser si esto es lo correcto o no. – mquander

+0

Tiendo a estar de acuerdo con estos comentarios. Aprender nuevos trucos y conceptos es programar como avanzar y respirar para un tiburón. Tienes que hacerlo o morirás. – andleer

4

Estoy más cómodo con la primera versión. Toma menos tiempo para leer y entender. Estoy de acuerdo en que los métodos de extensión también están bien si está familiarizado con él y también está familiarizado con el método With, pero ¿cuál es el beneficio de este en este caso?

5

Es un uso interesante de las extensiones, y lo aprecio solo por ese mérito. No estoy seguro de que lo use, pero si a su equipo le gusta, entonces por supuesto, úselo.

9

Un voto más para "no útil". El método de extensión With no hace nada excepto resumir sentencias secuenciadas con un método. C# ya tiene una función incorporada para las instrucciones de secuenciación, se llama ;.

De forma similar, el WithIf ajusta un enunciado if sin ninguna modificación al flujo de control. Desde mi punto de vista, solo te estás invitando a métodos como:

public static T For<T>(
    this T t, int start, Func<int, bool> cond, Action<T, int> f) 
{ 
    for(int i = start; cond(i); i++) 
    { 
     f(t, i); 
    } 
    return t; 
} 
1

Yo diría que sigas con la primera versión. Lo que has publicado es demasiado inteligente para ser inmediatamente útil para alguien que lee el código.

Usted podría incluso ir un paso más allá y acabar con eso "var":

DropDownList ddl = (DropDownList) Page.FindControl("ddlName"); 
+0

Acaba de presentar la repetición de DropDownList sin ningún motivo. Prefiero var ;-) –

+0

@ Meta-Knight: la razón es para favorecer la claridad/legibilidad del código, especialmente para aquellos que no están familiarizados con esa construcción C#. –

2

que predicen toda la moda "interfaz fluida" será la "notación húngara" de la década de 2000. Personalmente creo que no se ve muy limpio y corre el riesgo de volverse muy inconsistente si tiene múltiples desarrolladores, cada uno con sus propias preferencias.

6

El original es más legible.

El cambio de API sencilla sería la de hacer que el objeto devuelto por FindControl() una cosa del constructor-esque (donde vuelven todos los métodos set 'esto'):

Page.FindControl("LocationDropDownList") 
    .setVisible(true) 
    .setSelectedValue(isAdmin ? "111" : "123"); 
2

Parece que su compañero de trabajo es una Lambda Junkie.

+0

Sí. Gran adicto! – andleer

2

Creo que la cuestión de la legibilidad es subjetiva y personalmente no tengo ningún problema con lo que has hecho. Consideraría usarlo si su organización lo "aprobó".

Creo que el concepto es correcto y si cambió "Con" a "Deje" sería más "funcional" o "F # -ish". Opinión personal.

Page.FindControl("LocationDropDownList")  
    .CastAs<DropDownList>()  
    .Let(d => d.Visible = true) 
    .Let(d => d.SelectedValue = "123"); 
+1

Estoy completamente de acuerdo. – grenade

+0

Estoy de acuerdo, Let es probablemente un nombre mejor que With. Típico, generalmente no se trata del concepto, sino más bien de cómo nombrarlo. – andleer

3

Nota mínima. Por experiencia personal, que cambiaría:

if(isAdmin) 
    ddl.SelectedValue = "111"; 

a

if(isAdmin) { 
    ddl.SelectedValue = "111"; 
} 

o

if(isAdmin) 
{ 
    ddl.SelectedValue = "111"; 
} 

Esto le ahorrará tiempo en la depuración tarde o temprano.

+7

"Esto le ahorrará tiempo en la depuración tarde o temprano". Solo si su editor no tiene sangría automática. De lo contrario, prefiero sin llaves para expresiones de una sola línea. Me parece más limpio. – bsneeze

+0

Yo votaría por expresiones de una línea en la misma línea que la instrucción if – Scoregraphic

3

Si esto fuera un función idioma:

With(Page.FindControl("LocationDropDownList") as DropDownList) 
{ 
    Visible = true; 
    SelectedValue = "123"; 
    if(isAdmin)  
     Add(new ListItem("111")); 
} 

Se podría ganar algo:

  • redundancia evitar del objeto mutado
  • todas las características del lenguaje disponibles en el "con" bloque

Above tries emular el estilo sin cosechar los beneficios. Cargo Cult.

(Nota: Me hago entender los diversos argumentos en su contra, pero todavía estaría bien)


Por cierto, algunos de mi C++ Win32 interfaz de usuario Ayudantes contienen emisores que utilizan el encadenamiento similares lo que desea lograr:

LVItem (m_lc, idx) .SetText (_T ("Hola")). SetImg (12) .SetLParam (id);

En ese caso, al menos gano "sin redundancia", pero eso se debe a que no tengo propiedades.

+1

Me gusta esta respuesta. Es como agregar la sintaxis del inicializador, pero en el objeto existente. – Dolphin

1

Este es un caso de aprendizaje perfecto sobre cómo hacer algo más complicado de lo que debe ser.

La primera versión es clara y no requiere conocimiento adicional más allá de las construcciones de lenguaje normal.

0

En cuanto a una "Interfaz fluida" C# ya tiene una gran sintaxis para inicializadores que es (en mi humilde opinión) mejor que tratar de usar el estilo fluido. Por supuesto, en su ejemplo no está inicializando un nuevo objeto, está cambiando uno existente. Toda mi experiencia con interfaces fluidas proviene de una segunda exploración de wikipedia 30, pero creo que la respuesta de JeeBee es más en el espíritu de la programación Fluido, aunque podría cambiar las cosas un poco:

Page.FindDropDownList("LocationDropDownList")  
    .setVisible(true)  
    .setAdminSelectedValue("111") 
    .setSelectedValue("123") 

Se podría argumentar que esto es más legible, especialmente para un idioma sin propiedades, pero todavía creo que no tiene sentido en C#.

0

En ciertas circunstancias cuidadosamente interfaces fluidas construida puede ser muy útil. En primer lugar, dado que el desarrollador tiene un número limitado de opciones, son (típicamente) fáciles de usar y difíciles de usar de forma incorrecta. En segundo lugar, debido a la estructura de tipo oración, pueden ser una manera agradable y limpia de declarar sus intenciones, especialmente cuando se construyen objetos complejos.

he encontrado interfaces fluidas a ser muy útil en el desarrollo de código de prueba en el que a menudo es necesaria la construcción de una gran cantidad de objetos de dominio con ligeras variaciones. También los utilicé con éxito como una forma de presentar el patrón del decorador y eliminar la excesiva sobrecarga de métodos.

Si alguien está interesado en aprender más sobre interfaces fluidas, sugiero que veas este work in progress por Martin Fowler.

0

buena regla de oro:

Si su primera impresión de su código es "Este es inteligente" - no es probablemente una buena idea.

El código correcto debe ser simple, legible, y solo "inteligente" si es absolutamente necesario.

+1

Así que no quisiera esta 'for (var i = -1; ((++ i