¿C # se está volviendo más difícil de leer?

15

A medida que C # ha progresado, se han agregado muchas funciones de lenguaje. Ha llegado al punto en que se está volviendo ilegible para mí.

Como ejemplo, considere el siguiente fragmento de código de Caliburn. Código de micro aquí :

            container = CompositionHost.Initialize(
                   new AggregateCatalog(
                      AssemblySource.Instance.
                             Select(x => new AssemblyCatalog(x))
                               .OfType<ComposablePartCatalog>()
                )
            );

Ahora, este es solo un pequeño ejemplo.

Tengo un número de preguntas:

  • ¿Es este un problema común o conocido?
  • ¿La comunidad de C # está encontrando lo mismo?
  • ¿Es esto un problema con el idioma o es el estilo utilizado por el desarrollador?

¿Hay alguna solución simple para comprender mejor el código de otros y evitar escribir código de esta manera?

Steven Evers
fuente
66
Es la función lambda, son difíciles de leer hasta que realmente los obtienes.
Ryathal
99
Creo que realmente no conocías la sintaxis tan bien como pensabas. Con práctica, leer su ejemplo es simple.
ChaosPandion
66
@Thomas Owens: Hombre de mierda, al menos danos algo de tiempo para editar preguntas para mantenerlas abiertas. ¿15 minutos? Ven ahora.
Steven Evers
44
@DannyVarod: 1. "No" no es una respuesta aceptable 2. La pregunta está cerrada 3. Si está de acuerdo en que la pregunta no debería estar aquí, marque / vote para cerrarla o editarla para que sea mejor .
Steven Evers
2
@ Thorbjørn Ravn Andersen - ¿Qué se puede aprender de una bofetada? ¡Esto no contiene información!

Respuestas:

12

Una nota rápida sobre dónde está el lenguaje debería aclararlo: C # es un lenguaje de programación de propósito general ; a diferencia de C (como C ++) se esfuerza por lograr una alta abstracción; a diferencia de los dialectos de Lisp, su objetivo es la expresividad práctica y, a diferencia de Java, es más agresivo: Microsoft responde rápidamente a la demanda.

Es por eso que se está convirtiendo en una mezcla de LINQ, lambdas y nuevas palabras clave extrañas: se está adaptando a nuevos dominios problemáticos rápidamente, y esta es una pendiente resbaladiza hacia un lenguaje tan complejo que muy pocos pueden usarlo correctamente (como C ++). No es un problema con C # en sí, es un problema con cualquier lenguaje con estos ambiciosos objetivos.

La comunidad es consciente de esto y, lo que es más importante, los muchachos detrás de C # son muy conscientes de esto (las pocas entradas de blog y podcasts sobre lo que estaba detrás de las nuevas incorporaciones en C # 5.0 muestran lo mal que estos tipos quieren mantener las cosas simples). Microsoft está tratando de quitar parte de la carga de su buque insignia para que no se convierta en un tarpit: la introducción del DLR, un foco brillante sobre nuevos idiomas (F #).

Además, el material del curso sobre C # (incluidas las certificaciones de MS) recomienda diferentes (pero consistentes) estilos de uso para C # según el problema: elija su arma y manténgala: LINQ de estilo fluido en algunos problemas, lambdas con TPL en otros, simple Viejo para la mayoría.

vski
fuente
2
¿Puedes explicar lo que quieres decir con "a diferencia de los dialectos de Lisp, apunta a la expresividad práctica"?
Giorgio
27

No. C # nos da más opciones para escribir código de manera más sucinta. Esto puede ser usado o abusado. Si se usa correctamente, hace que el código sea más fácil de leer. Si se usa incorrectamente, puede hacer que el código sea más difícil de leer. Pero los malos programadores siempre han tenido la habilidad de escribir código difícil de leer.

Tomemos dos ejemplos, ambos basados ​​en ejemplos encontrados en StackOverflow con respecto al mismo problema, encontrando tipos con un atributo específico:

C # 2.0:

static IEnumerable<Type> GetTypesWithAttribute<TAttribute>(bool inherit) 
                              where TAttribute: System.Attribute
{
    foreach(Assembly assembly in AppDomain.Current.GetAssemblies())
    {
        foreach(Type type in assembly.GetTypes()) 
        {
            if (type.IsDefined(typeof(TAttribute),inherit))
                yield return type;
        }
    }
}

C # 4.0:

IEnumerable<Type> GetTypesWith<TAttribute>(bool inherit) 
                              where TAttribute: System.Attribute
{ 
    return from assembly in AppDomain.CurrentDomain.GetAssemblies()
           from type in assembly.GetTypes()
           where type.IsDefined(typeof(TAttribute),inherit)
           select type;
}

En mi humilde opinión, la nueva sintaxis hace que sea mucho más fácil de leer.

Pete
fuente
la nueva sintaxis es de hecho más fácil de leer, pero más fácil de comprender, como en lo que sucede debajo del capó. El primer ejemplo es comprensible, el segundo es más legible.
nawfal
44
@nawfal En mi experiencia, la gente tiene muchos más problemas para comprender las construcciones de "rendimiento de rendimiento" que las declaraciones linq; entonces diría que el segundo es más legible y comprensible. Ninguno de los dos le dice realmente qué está pasando bajo el capó, ya que ambos se relacionan con abstracciones lejos de cómo funcionan realmente los procesadores.
Chuu
Para ser honesto, no me gusta LINQ porque abstrae los bucles y alienta a los codificadores a recuperar datos en consultas LINQ separadas (con bucles separados en el fondo) en lugar de un bucle con un núcleo más complicado.
mg30rg
8

Cuando se agregó LINQ, popularizó un estilo de codificación que involucra muchos encadenamientos de métodos de estilo fluido y el paso de lambdas como parámetros.

Este estilo es muy poderoso una vez que te sientas cómodo con él. Sin embargo, se puede abusar para hacer un código bastante ilegible. No creo que su ejemplo sea demasiado malo, aunque la sangría es algo aleatoria (y esa es una característica común de este estilo de código, Visual Studio no lo sangra automáticamente).

En mi lugar de trabajo, discutimos este estilo de codificación al revisar nuestros estándares de codificación a principios de este año, y decidimos alentar a los desarrolladores a dividir el código de esa manera: específicamente, no crear e inicializar objetos anónimos dentro de una llamada de función. Entonces su código se convertiría en:

var assemblyCatalog = AssemblySource.Instance
    .Select(x => new AssemblyCatalog(x))
    .OfType<ComposablePartCatalog>();
var aggregateCatalog = new AggregateCatalog(assemblyCatalog);
container = CompositionHost.Initialize(aggregateCatalog);
Carson63000
fuente
1
¿No es extraño crear un nuevo tipo y luego activarlo dinámicamente después?
DeadMG
Probablemente, simplemente corté el fragmento de código sin ninguna consideración real de cuál era su intención. Solo quería dividir todas las instancias en sus propias líneas de código para mostrar qué efecto tendría en el aspecto del código.
Carson63000
Me responsabilizo por la sangría :(). Aquí está el original: devlicio.us/blogs/rob_eisenberg/archive/2010/07/08/…
1
Estoy totalmente de acuerdo con esa respuesta. El problema en el fragmento de código de ejemplo no es la nueva sintaxis de C #, es que el escritor intentó ser inteligente con un código de "una línea". Existe esta vieja regla que data de UNIX: todo debería hacer solo una cosa y hacerlo bien. Se aplica a la clase, se aplica al método y, por supuesto, ciertamente se aplica a las líneas de código.
Laurent Bourgault-Roy
4

El código en su ejemplo no es fácil de leer porque

  • Mezcla muchas nociones (Composición, Catálogos, Agregados, Ensamblajes, ComposableParts ...) con varios niveles de anidamiento, mientras que el código de llamada normalmente debe tratar con un solo nivel. Parece un poco una violación de la Ley de Demeter solo con llamadas a métodos anidados en lugar de una cadena de sub-sub-propiedades. Esto confunde un poco la intención detrás de la línea de código.

  • Hay un uso AssemblySource.Instance.Select()único de singleton: implica que Instance es un IEnumerable, lo cual es un poco incómodo.

  • La variable x en la expresión lambda es fea. Por lo general, intenta dar a sus variables lambda nombres reveladores de intención: ayuda al lector a identificar de qué tipo de datos se trata la función, incluso si no está familiarizado con lambdas.

  • No está claro por qué debería filtrar con OfType<T>()una colección de objetos que acaba de actualizar ...

Sin embargo, todos estos defectos se relacionan con la forma en que el desarrollador original escribió el código y cuán expresivo lo diseñó para ser. Esto no es algo específico de las últimas versiones de C # y la aparición de expresiones lambda.

De manera más general, ayuda si el lector de su código sabe cómo funcionan las lambdas, pero con un nombre lo suficientemente claro, casi siempre logra que su código sea legible incluso para alguien con antecedentes previos a .NET 3.5.

guillaume31
fuente
2

Cada vez que una nueva versión del lenguaje popular adquiere nuevas construcciones, surgen debates similares.

También tuve estas dudas hace años (http://gsscoder.blogspot.it/2009/08/use-csharps-features-wisely.html).

En mi opinión, C # está evolucionando de una manera elegante y consistente.

El código en su muestra no es complejo, tal vez no esté acostumbrado a expresiones lamda.

El problema es que C # no es solo un lenguaje orientado a objetos, ahora también admite construcciones funcionales (http://archive.msdn.microsoft.com/FunctionalCSharp/).

gsscoder
fuente
1

Me tomó un tiempo entender la sintaxis de Lambda, y soy de un fondo de matemáticas y física. Es un poco similar a las expresiones matemáticas, aunque usan -> en lugar de =>:

Ejemplo matemático f (x): = x-> x + 2 esto se lee como "La función f de x se define como x asigna a x + 2

c # ejemplo del myDelegate = x => x +2; esto se lee como "myDelegate es una función tal que myDelegate (x) asigna x a x + 2

La inconsistencia entre las matemáticas y la programación realmente no ayuda, aunque supongo que -> ya se había utilizado en C ++ como "propiedad de" (¡urrgghh!)

http://en.wikipedia.org/wiki/List_of_mathematical_symbols

Andy R
fuente
"aunque supongo que -> ya se había incluido en C ++ como" propiedad de "(¡urrgghh!)" ¡ Como si! Mal en C ++ significa "propiedad de los asignados dinámicamente ...". Si algo no es dinámico, usa el punto como en cualquier otro idioma.
mg30rg