2010-05-17 20 views
21

No en términos de legibilidad, naturalmente, ya que siempre puede organizar los métodos separados en líneas separadas. Más bien, ¿es peligroso, por alguna razón, encadenar un número excesivamente grande de métodos? Uso el método de encadenamiento principalmente para ahorrar espacio al declarar variables individuales de un solo uso y, tradicionalmente, utilizar métodos de retorno en lugar de métodos que modifican al que llama. Excepto por los métodos de cuerda, esos me encadenan sin piedad. En cualquier caso, a veces me preocupa el impacto del uso de cadenas de métodos excepcionalmente largas, todo en una línea.¿Puede una cadena de métodos de C# ser "demasiado larga"?

Digamos que necesito actualizar el valor de un elemento basado en el nombre de usuario de alguien. Lamentablemente, el método más corto para recuperar al usuario correcto se ve de la siguiente manera.

SPWeb web = GetWorkflowWeb(); 
SPList list2 = web.Lists["Wars"]; 
SPListItem item2 = list2.GetItemById(3); 
SPListItem item3 = item2.GetItemFromLookup("Armies", "Allied Army"); 
SPUser user2 = item2.GetSPUser("Commander"); 
SPUser user3 = user2.GetAssociate("Spouse"); 
string username2 = user3.Name; 
item1["Contact"] = username2; 

Todo con un 2 o 3 tiene una duración de sólo una llamada, por lo que podría condensarse como la siguiente (que también me permite deshacerse de un aspirante a superflua 1):

SPWeb web = GetWorkflowWeb(); 
item["Contact"] = web.Lists["Armies"] 
        .GetItemById(3) 
        .GetItemFromLookup("Armies", "Allied Army") 
        .GetSPUser("Commander") 
        .GetAssociate("Spouse") 
        .Name; 

Es cierto que parece mucho más tiempo cuando todo está en una línea y cuando tiene int.Parse(ddlArmy.SelectedValue.CutBefore(";#", false)) en lugar de 3. Sin embargo, esta es una de las longitudes promedio de estas cadenas, y puedo prever fácilmente algunos recuentos excepcionalmente más largos. Excluyendo la legibilidad, ¿hay algo de lo que deba preocuparme estas 10+ cadenas de métodos? ¿O no hay daño al usar cadenas de métodos muy largas?

+3

Diez no es nada. Si tuvieras más de, digamos, diez mil, podrías empezar a tener problemas. Dijiste que haces esto para "ahorrar espacio". No entiendo qué espacio es el que cree que está salvando, o por qué cree que ese espacio es un recurso escaso que requiere una supervisión cuidadosa. ¿Puede explicar qué evidencia empírica respalda su creencia de que está ahorrando en el uso de un recurso escaso o costoso? –

+7

No soy un experto, pero creo que su segundo ejemplo es mucho más legible. Si tuviera la tarea de mantener ese ejemplo, entendería mucho más rápidamente lo que estaba haciendo el segundo. Hay mucho menos código superfluo. Imagino que la legibilidad debería ser la principal preocupación aquí. Si sus cadenas son más fáciles de leer, eso es bueno –

+0

@Eric No he afirmado que algo sea un recurso escaso o costoso. El espacio que estoy guardando es un par de pésimos bytes de espacio de archivos en el archivo de código para indicar "; \ r \ nCLASS bar = foo" entre los métodos encadenados, que a su vez me parece más limpio. Teniendo en cuenta que me parece limpio es cuestión de legibilidad y * excluyo explícitamente la legibilidad como una preocupación *, ¿de dónde sacas que encuentro que este espacio requiere "cuidado cuidadoso"? –

Respuesta

33

No hay limitaciones técnicas sobre cuánto tiempo puede ser una cadena de método.

Sin embargo, tres áreas que pueden convertirse en problemattic son depuración, manejo excepción, y disposición recursos.

La depuración se complica por el mismo hecho que hace que el encadenamiento sea tan elegante: la falta de variables temporales intermedias. Lamentablemente, sin variables temp, inspeccionar los resultados intermedios cuando la depuración se vuelve dolorosa.

El manejo de excepciones se complica por el hecho de que no se pueden aislar las excepciones planteadas de un método frente a otro. Normalmente, esto no es un problema si no puede hacer algo significativo en respuesta a la excepción, simplemente deje que se propague por la cadena de llamadas. Sin embargo, si luego se da cuenta de que necesita manejo de excepciones, debe refactorizar la sintaxis de encadenamiento para poder insertar manejadores de prueba/captura apropiados.

Similar al manejo de excepciones es el caso de la eliminación determinista de los recursos. La manera más fácil en C# para lograr esto es con using() - desafortunadamente, la sintaxis de encadenamiento lo impide. Si está llamando a métodos que devuelven objetos desechables, probablemente sea una buena idea evitar la sintaxis de encadenamiento para que pueda ser un buen "ciudadano de códigos" y disponer de esos recursos lo antes posible.

La sintaxis del método de encadenamiento se usa a menudo en fluent APIs, donde permite que la sintaxis de su código refleje más de cerca la secuencia de operaciones que desea. LINQ es un ejemplo en .NET donde a menudo se ve la sintaxis de fluidez/encadenamiento.

+0

de acuerdo, estaba a punto de publicar el mismo – SirLenz0rlot

4

La única conserjería que debe considerar al respecto (si ignora la legibilidad) es la gestión de recursos y GC. Las preguntas que debe formular son.

  • ¿Las llamadas que realizo son devolver objetos que deberían eliminarse?
  • ¿Estoy insertando muchas cosas dentro de un solo alcance en lugar de ámbitos más pequeños en los que el GC podría ser más reactivo? (El GC está programado para ejecutarse cada vez que abandona un alcance, aunque la programación y cuando realmente se ejecuta son dos cosas diferentes :-p).

Pero en realidad ... el tiempo en que se llama a un método por línea, o se enchufa a todos juntos, todo termina siendo el mismo en IL (o casi el mismo).

Josh

+1

El GC no está programado para ejecutarse cada vez que abandona un ámbito: se ejecuta cuando no hay espacio libre en el montón administrado. – Lee

+0

es ese el caso? Sé que cuando estaba probando el uso de distutores para administrar un conjunto de singleton (hace alrededor de 3 años) se llamaba al distructor cuando dejé el endoscopio independientemente del tamaño del montón (es decir, casi ninguno). Y sé que se llama el distructor (cuando presente) por el primer pase de GC .. –

7

legibilidad es la mayor preocupación, pero a menudo que no es un problema en absoluto.

También podría describir la sintaxis de la consulta LINQ como (debajo de todo) exactamente tal configuración. Sólo hace que se vea más bonita ;-P

Una posible cuestión es donde tiene que introducir cosas como using o lock; con la API fluida, puede caer en la tentación de simplemente descartar estos componentes, pero eso puede provocar extravagancias cuando se lanza una excepción.

La otra posible idea es que es posible que desee tener un manejo de excepciones más granular en algunas de las llamadas; pero siempre puede romper el flujo:

var foo = bar.MethodA().MethodB(...).MethodC(); 
try { 
    foo.MethodD(); 
} catch (SomeSpecificException) { 
    //something interesting 
} 

O incluso se podría hacer que en un método de extensión para mantener la apariencia fluidez:

bar.MethodA().MethodB(...).MethodC().MyExtensionMethodD(); 

donde MyExtensionMethodD es la que se agrega, con especial manejo (excepciones, bloqueos, uso, etc.).

6

Esto es considerado un código olor por algunos y no por otros. Cada vez que vea lo siguiente:

Foo.getBar().getBlah().getItem().getName(); 

realmente debería estar pensando, "¿qué es lo que realmente quiero?" En cambio, tal vez su método debe contener una llamada a función:

String getName(Int _id, String _item) 
{ 
    return myBar.getName(_id, _item); 
} 

que luego delega hacia abajo, entre las clases. Luego, si algo cambia en una de sus clases en una actualización posterior, verá exactamente dónde ocurre y puede cambiarlo en una ubicación.

Cuestiones relacionadas