¿Por qué no usarías la directiva 'using' en C #?

71

Los estándares de codificación existentes en un gran proyecto de C # incluyen una regla de que todos los nombres de tipo deben estar completamente calificados, lo que prohíbe el empleo de la directiva 'usar'. Entonces, en lugar de lo familiar:

using System.Collections.Generic;

.... other stuff ....

List<string> myList = new List<string>();

(Probablemente no sea sorprendente que vartambién esté prohibido).

Termino con:

System.Collections.Generic.List<string> myList = new System.Collections.Generic.List<string>();

Eso es un aumento del 134% en la escritura, sin que ninguno de esos aumentos proporcione información útil. En mi opinión, el 100% del aumento es ruido (desorden) que realmente impide la comprensión.

En más de 30 años de programación, he visto un estándar así propuesto una o dos veces, pero nunca implementado. La razón detrás de esto se me escapa. La persona que impone el estándar no es estúpida, y no creo que sea maliciosa. Lo que deja equivocada como la única otra alternativa a menos que me falte algo.

¿Alguna vez has oído hablar de que se impone un estándar de este tipo? Si es así, ¿cuál fue la razón detrás de esto? ¿Puedes pensar en otros argumentos que no sean "es estúpido" o "todos los demás emplean using" que podrían convencer a esta persona de eliminar esta prohibición?

Razonamiento

Los motivos de esta prohibición fueron:

  1. Es engorroso pasar el mouse sobre el nombre para obtener el tipo completo. Es mejor tener siempre visible el tipo totalmente calificado todo el tiempo.
  2. Los fragmentos de código enviados por correo electrónico no tienen el nombre completo y, por lo tanto, pueden ser difíciles de entender.
  3. Al ver o editar el código fuera de Visual Studio (Notepad ++, por ejemplo), es imposible obtener el nombre de tipo completo.

Mi opinión es que los tres casos son raros, y que hacer que todos paguen el precio del código desordenado y menos comprensible solo para acomodar algunos casos raros es erróneo.

Los posibles problemas de conflicto de espacio de nombres, que esperaba que fueran la principal preocupación, ni siquiera se mencionaron. Eso es especialmente sorprendente porque tenemos un espacio de nombres MyCompany.MyProject.Core, que es una idea especialmente mala. Hace mucho tiempo aprendí que nombrar cualquier cosa Systemo Coreen C # es un camino rápido hacia la locura.

Como otros han señalado, los conflictos de espacio de nombres se manejan fácilmente mediante refactorización, alias de espacio de nombres o calificación parcial.

Jim Mischel
fuente
Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
maple_shaft
1
Un ejemplo del mundo real de por qué podría ser una buena idea (pero en el mundo C ++): una respuesta a ¿Por qué "usar el espacio de nombres estándar" se considera una mala práctica? , cerca de "que las directivas y las declaraciones 'que usan' están prohibidas".
Peter Mortensen
Antes de leer la sección de Razonamiento, adiviné las razones poco prácticas porque mi empresa tiene reglas similares.
Gqqnbig

Respuestas:

69

La pregunta más amplia:

¿Alguna vez has oído hablar de una norma tan impuesta? Si es así, ¿cuál fue la razón detrás de esto?

Sí, he oído hablar de esto, y el uso de nombres de objetos totalmente calificados evita las colisiones de nombres. Aunque es raro, cuando suceden, pueden ser excepcionalmente espinosos de entender.


Un ejemplo: ese tipo de escenario probablemente se explica mejor con un ejemplo.

Digamos que tenemos dos Lists<T>pertenecientes a dos proyectos separados.

System.Collections.Generic.List<T>
MyCorp.CustomCollections.Optimized.List<T>

Cuando usamos el nombre del objeto totalmente calificado, queda claro cuál List<T>se está utilizando. Esa claridad obviamente tiene el costo de la verbosidad.

Y podría estar discutiendo: "¡Espera! ¡Nadie usaría nunca dos listas como esa!" Que es donde señalaré el escenario de mantenimiento.

Usted es un módulo escrito Foopara su corporación que utiliza la corporación aprobada, optimizada List<T>.

using MyCorp.CustomCollections.Optimized;

public class Foo {
    List<object> myList = ...;
}

Más tarde, un nuevo desarrollador decide extender el trabajo que ha estado haciendo. Sin estar al tanto de los estándares de la compañía, actualizan el usingbloque:

using MyCorp.CustomCollections.Optimized;
using System.Collections.Generic;

Y puedes ver cómo las cosas van mal a toda prisa.

Debería ser trivial señalar que podría tener dos clases propietarias del mismo nombre pero en diferentes espacios de nombres dentro del mismo proyecto. Por lo tanto, no se trata solo de una colisión con las clases proporcionadas por .NET Framework.

MyCorp.WeightsAndLengths.Measurement();
MyCorp.TimeAndSpace.Measurement();

La realidad:
ahora, ¿es probable que esto ocurra en la mayoría de los proyectos? No en realidad no. Pero cuando trabajas en un proyecto grande con muchas entradas, haces todo lo posible para minimizar las posibilidades de que las cosas exploten en ti.

Los grandes proyectos con múltiples equipos contribuyentes son un tipo especial de bestia en el mundo de las aplicaciones. Las reglas que parecen irrazonables para otros proyectos se vuelven más pertinentes debido a los flujos de entrada al proyecto y la probabilidad de que los contribuyentes no hayan leído las pautas del proyecto.

Esto también puede ocurrir cuando dos grandes proyectos se fusionan. Si ambos proyectos tenían clases con nombres similares, verá colisiones cuando comience a hacer referencia de un proyecto a otro. Y los proyectos pueden ser demasiado grandes para refactorizar o la gerencia no aprobará el gasto para financiar el tiempo dedicado a la refactorización.


Alternativas:
Si bien no preguntó, vale la pena señalar que esta no es una gran solución al problema. No es una buena idea crear clases que colisionen sin sus declaraciones de espacio de nombres.

List<T>, en particular, debe tratarse como una palabra reservada y no como el nombre de sus clases.

Del mismo modo, los espacios de nombres individuales dentro del proyecto deben esforzarse por tener nombres de clase únicos. Tener que tratar de recordar con qué espacio de nombres Foo()está trabajando es una sobrecarga mental que es mejor evitar. Dicho de otra manera: tener MyCorp.Bar.Foo()y MyCorp.Baz.Foo()va a hacer tropezar a sus desarrolladores y es mejor evitarlos.

Como mínimo, puede usar espacios de nombres parciales para resolver la ambigüedad. Por ejemplo, si no puede renombrar ninguna de las Foo()clases, puede usar sus espacios de nombres parciales:

Bar.Foo() 
Baz.Foo()

Razones específicas para su proyecto actual:

Actualizó su pregunta con los motivos específicos que recibió para su proyecto actual siguiendo ese estándar. Echemos un vistazo a ellos y realmente divaguemos por el sendero del conejito.

Es engorroso pasar el mouse sobre el nombre para obtener el tipo completo. Es mejor tener siempre visible el tipo totalmente calificado todo el tiempo.

"¿Incómodo?" Mmm no. Molesto tal vez. Mover unas onzas de plástico para cambiar el puntero en pantalla no es engorroso . Pero yo divago.

Esta línea de razonamiento parece más un encubrimiento que otra cosa. Por supuesto, supongo que las clases dentro de la aplicación tienen un nombre débil y debe confiar en el espacio de nombres para obtener la cantidad adecuada de información semántica que rodea el nombre de la clase.

Esta no es una justificación válida para nombres de clase totalmente calificados, quizás sea una justificación válida para usar nombres de clase parcialmente calificados.

Los fragmentos de código enviados por correo electrónico no tienen el nombre completo y, por lo tanto, pueden ser difíciles de entender.

Esta línea de razonamiento (¿continua?) Refuerza mi sospecha de que las clases actualmente están mal nombradas. Nuevamente, tener nombres de clase pobres no es una buena justificación para requerir que todo use un nombre de clase completamente calificado. Si el nombre de la clase es difícil de entender, hay mucho más mal que lo que pueden arreglar los nombres de clase completamente calificados.

Al ver o editar el código fuera de Visual Studio (Notepad ++, por ejemplo), es imposible obtener el nombre de tipo completo.

De todas las razones, esta casi me hizo escupir mi bebida. Pero de nuevo, me estoy desviando.

Me pregunto por qué el equipo edita o visualiza código con frecuencia fuera de Visual Studio. Y ahora estamos viendo una justificación que es bastante ortogonal a lo que los espacios de nombres deben proporcionar. Este es un argumento respaldado por herramientas, mientras que los espacios de nombres están ahí para proporcionar una estructura organizativa al código.

Parece que el proyecto propio sufre de convenciones de nomenclatura deficientes junto con desarrolladores que no están aprovechando lo que las herramientas pueden proporcionarles. Y en lugar de resolver esos problemas reales, han intentado aplicar una tirita sobre uno de los síntomas y requieren nombres de clase completamente calificados. Creo que es seguro clasificar esto como un enfoque equivocado.

Dado que hay clases mal nombradas, y suponiendo que no pueda refactorizar, la respuesta correcta es usar el IDE de Visual Studio para su máximo beneficio. Posiblemente considere agregar un complemento como el paquete VS PowerTools. Luego, cuando estoy mirando AtrociouslyNamedClass(), puedo hacer clic en el nombre de la clase, presionar F12 y ser llevado directamente a la definición de la clase para comprender mejor lo que está tratando de hacer. Del mismo modo, puedo hacer clic en Shift-F12 para encontrar todos los puntos en el código que actualmente tienen que usar AtrociouslyNamedClass().

Con respecto a las preocupaciones de herramientas externas, lo mejor que puede hacer es simplemente detenerlo. No envíe fragmentos de correo electrónico de un lado a otro si no están claros de inmediato a qué se refieren. No use otras herramientas fuera de Visual Studio, ya que esas herramientas no tienen la inteligencia que rodea el código que su equipo necesita. Notepad ++ es una herramienta increíble, pero no está hecha para esta tarea.

Así que estoy de acuerdo con su evaluación con respecto a las tres justificaciones específicas que se le presentaron. Dicho esto, lo que creo que le dijeron fue "Tenemos problemas subyacentes en este proyecto que no pueden / no abordarán y así es como los" solucionamos ". Y eso obviamente habla de problemas más profundos dentro del equipo que pueden servir como señales de alerta para usted.


fuente
1
+1. También he encontrado algunas colisiones desagradables en nuestros espacios de nombres internos. Tanto al portar código heredado a un proyecto "2.0", como solo con unos pocos nombres de clase que son semánticamente válidos en diferentes contextos de espacio de nombres. Lo realmente complicado es que dos cosas con el mismo nombre a menudo tendrán interfaces similares, por lo que el compilador no se quejará ... ¡su tiempo de ejecución lo hará!
svidgen
23
Este problema se resuelve mediante el uso de alias de espacio de nombres, no mediante la imposición de reglas tontas que requieren código desordenadas con el uso explícito del espacio de nombres.
David Arno
44
@DavidArno - No estoy en desacuerdo contigo. Sin embargo, los proyectos grandes pueden no tener esa opción. Simplemente estoy señalando por qué un gran proyecto puede imponer esa regla. Sin embargo, no estoy abogando por la regla. Solo que a veces es necesario porque el equipo no tiene otras opciones.
44
@ GlenH7 es posible que desee especificar las otras soluciones disponibles en su respuesta. Odiaría ver a la gente tropezar con esto y pensar "¡Oh. Esa es una gran idea!" +1 para el objetivo restante y pragmático sin embargo.
RubberDuck
3
@ JimMischel - Parece que la regla se está utilizando como una muleta para algo . Sin embargo, puede que no sea posible determinar qué es esa cosa. En un nivel alto, sí, a veces hay justificación para no permitir, usingspero no parece que su proyecto califique como uno de esos casos.
16

Trabajé en una compañía donde tenían muchas clases con el mismo nombre, pero en diferentes bibliotecas de clases.

Por ejemplo,

  • Dominio Cliente
  • Legado.Cliente
  • Servicio X.Cliente
  • ViewModel.Customer
  • Base de datos Cliente

Podría decirse que es un mal diseño, pero sabe cómo se desarrollan orgánicamente estas cosas y cuál es la alternativa: cambiar el nombre de todas las clases para incluir el espacio de nombres. ¿Refactorización importante de todo?

En cualquier caso, había varios lugares donde los proyectos que hacían referencia a todas estas bibliotecas diferentes necesitaban usar múltiples versiones de la clase.

Con el usingdirectamente en la parte superior, esto fue un gran dolor en el culo, ReSharper haría cosas extrañas para intentar que funcionara, reescribiendo las usingdirectivas sobre la marcha, obtendría el Cliente no puede ser lanzado como ... Errores de estilo del cliente y no saber qué tipo era el correcto.

Entonces, teniendo al menos el espacio de nombres parcial,

var cust = new Domain.Customer

o

public void Purchase(ViewModel.Customer customer)

mejoró enormemente la legibilidad del código y lo hizo explícito qué objeto deseaba.

No era un estándar de codificación, no era el espacio de nombres completo, y no se aplicaba a todas las clases como estándar.

Ewan
fuente
13
"¿Cuál es la alternativa, cambiar el nombre de todas las clases para incluir el espacio de nombres? ¿Refactorización importante de todo?" Sí, esa es la alternativa (correcta).
David Arno
2
Solo a corto plazo. Es mucho menos costoso que usar espacios de nombres explícitos en el código a largo plazo.
David Arno
3
Feliz de aceptar ese argumento, siempre y cuando me pague en efectivo, no en acciones.
Ewan
8
@David: No, NO es la alternativa correcta. Calificar completamente todos los identificadores o al menos aquellos que realmente colisionan es la forma correcta y la razón por la que existen espacios de nombres es para proporcionar colisiones cuando se usa el mismo nombre en diferentes módulos. El punto es que algunos de los módulos pueden ser de terceros, cuya base de código no puede modificar. Entonces, ¿qué haces cuando los identificadores de dos bibliotecas de terceros diferentes colisionan y necesitas usar ambas bibliotecas, pero no puedes refactorizar ninguna de ellas? Existen espacios de nombres, porque la refactorización no siempre es posible.
Kaiserludi
44
@CarlSixsmith, si uno se suscribe a las opiniones de Martin Fowler sobre la refactorización, entonces si el cambio impacta al usuario, entonces está en lo correcto, no es refactorización; Es otra forma de reestructuración. Sin embargo, esto plantea dos puntos: 1. ¿Todos los métodos públicos son automáticamente parte de la API? 2. El propio Fowler reconoce que está luchando una batalla perdida por esta definición, con un número creciente de personas que usan el término "refactorización" para referirse a la reestructuración generalizada, incluidos los cambios públicos.
David Arno
7

No es bueno ser demasiado detallado o demasiado corto: uno debe encontrar un medio dorado para ser lo suficientemente detallado como para evitar errores sutiles, evitando al mismo tiempo escribir demasiado código.

En C #, un ejemplo de eso son los nombres de los argumentos. Encontré varias veces un problema en el que pasé horas buscando una solución, mientras que un estilo de escritura más detallado podría evitar el error en primer lugar. El problema consiste en un error en el orden de los argumentos. Por ejemplo, ¿te parece bien?

if (...) throw new ArgumentNullException("name", "The name should be specified.");
if (...) throw new ArgumentException("name", "The name contains forbidden characters.");

A primera vista, se ve perfectamente bien, pero hay un error. Las firmas de los constructores de las excepciones son:

public ArgumentException(string message, string paramName)
public ArgumentNullException(string paramName, string message)

¿Notó el orden de los argumentos?

Al adoptar un estilo más detallado donde se especifica el nombre de un argumento cada vez que el método acepta dos o más argumentos del mismo tipo , evitamos que el error vuelva a ocurrir, y así:

if (...) throw new ArgumentNullException(paramName: "name", message: "The name should be specified.");
if (...) throw new ArgumentException(paramName: "name", message: "The name contains forbidden characters.");

obviamente es más largo de escribir, pero resulta en menos tiempo de depuración desperdiciado.

Empujado a los extremos, uno podría forzar el uso de argumentos con nombre en todas partes , incluso en métodos como doSomething(int, string)donde no hay forma de mezclar el orden de los parámetros. Esto probablemente dañará el proyecto a largo plazo, resultando en mucho más código sin el beneficio de prevenir el error que describí anteriormente.

En su caso, la motivación detrás del “No usingregla” es que debe hacer que sea más difícil de usar el tipo incorrecto, como por ejemplo MyCompany.Something.List<T>vs System.Collections.Generic.List<T>.

Personalmente, evitaría esa regla porque, en mi humilde opinión, el costo del código difícil de leer está muy por encima del beneficio de un riesgo ligeramente reducido de mal uso del tipo incorrecto, pero al menos, hay una razón válida para esta regla.

Arseni Mourzenko
fuente
Situaciones como estas pueden ser detectadas por herramientas. ReSharper marca el paramNamecon el InvokerParameterNameatributo y emite una advertencia si no existe dicho parámetro.
Johnbot
2
@Johnbot: ciertamente pueden, pero en un número muy limitado de casos. ¿Qué pasa si tengo un SomeBusiness.Something.DoStuff(string name, string description)? Ninguna herramienta es lo suficientemente inteligente como para comprender qué es un nombre y qué es una descripción.
Arseni Mourzenko
@ArseniMourzenko en este caso, incluso los humanos necesitan tomarse un tiempo para determinar si la invocación es correcta o no.
Gqqnbig
6

Los espacios de nombres le dan al código una estructura jerárquica, permitiendo nombres más cortos.

   MyCompany.Headquarters.Team.Leader
   MyCompany.Warehouse.Team.Leader

Si permite la palabra clave "usar", hay una cadena de eventos ...

  • Todos realmente están usando un espacio de nombres global
    (porque incluyen todos los espacios de nombres que puedan desear)
  • La gente comienza a tener conflictos de nombres, o
  • Preocuparse por los enfrentamientos de nombres

Entonces, para evitar el riesgo, todos comienzan a usar nombres realmente largos:

   HeadquartersTeamLeader
   WarehouseTeamLeader

La situación se intensifica ...

  • Es posible que otra persona ya haya elegido un nombre, por lo que utiliza uno más largo.
  • Los nombres se alargan cada vez más.
  • Las longitudes pueden exceder los 60 caracteres, sin máximo, se vuelve ridículo.

He visto que esto sucede, así que puedo simpatizar con las compañías que prohíben el "uso".


fuente
11
Prohibir usinges la opción nuclear. Los alias de espacio de nombres y la calificación parcial son preferibles.
Jim Mischel
1

El problema con usinges que mágicamente se agrega al espacio de nombres sin una declaración explícita. Esto es un problema mucho mayor para el programador de mantenimiento que encuentra algo como List<>y sin estar familiarizado con el dominio, no sabe qué usingcláusula lo ha introducido.

Un problema similar ocurre en Java si uno escribe en import Java.util.*;lugar de, import Java.util.List;por ejemplo; la última declaración documenta qué nombre se ha introducido para que cuando List<>ocurra más tarde en la fuente, su origen se conozca a partir de la importdeclaración.

Michael Montenero
fuente
66
Si bien es cierto que alguien que no esté familiarizado con el dominio tendrá algunas dificultades, todo lo que tiene que hacer es pasar el mouse sobre el nombre del tipo y obtendrá el nombre completo. O puede hacer clic derecho e ir directamente a la definición de tipo. Para los tipos de .NET Framework, probablemente ya sepa dónde están las cosas. Para los tipos específicos de dominio, le llevará quizás una semana sentirse cómodo. En ese punto, los nombres totalmente calificados son solo ruido que impide la comprensión.
Jim Mischel
Jim ... Intenté pasar el mouse sobre una definición y no funcionó. ¿Ha considerado la posibilidad de que algunos programadores del mundo no usen IDE de Microsoft? En cualquier caso, su contador no invalida mi punto, que la fuente con el uso ya no es autodocumentada
Michael Montenero
2
Sí, hay personas que trabajan en C # que se perjudican al no utilizar las mejores herramientas disponibles. Y, mientras que el argumento de que la calificación completa es "autodocumentada" tiene algún mérito, dicho código tiene un costo enorme en legibilidad. Todo ese código extraño crea ruido que oscurece las partes del código que realmente importan.
Jim Mischel