C # - ¿Por qué se desalientan los prefijos en los campos?

19

En los viejos tiempos, hicimos notación húngara. Eso ahora se considera pasado y, en su mayor parte, ya no lo uso, pero todavía encuentro uso para el m_prefijo para indicar campos de miembros.

Para mí, si estoy leyendo el código de otra persona y veo esto:

count = 3;

Supongo que countes una variable local para esa función y estoy haciendo algo que se usará en otra parte de la función; si veo esto:

m_count = 3;

Inmediatamente me doy cuenta de que estoy actualizando el estado del objeto.

Las pautas de estilo de Microsoft dicen que esa es la forma incorrecta de hacer las cosas. Se supone que debo nombrar mis campos no públicos exactamente como las variables temporales definidas dentro de la función. No m_, ni siquiera un simple guión bajo.

Sí, podemos definir nuestro propio estilo de codificación, pero luego tengo que luchar con varias herramientas de análisis de código estático, para convencerlos de que nuestra forma de hacer las cosas está bien.

Estoy feliz de cambiar al estilo de Microsoft, pero me gustaría saber por qué las cosas son como son.

¿Por qué ahora se considera malo saber si una variable es una función local o un miembro?

PD Esto es muy similar a ¿Cuáles son las mejores prácticas actuales consideradas con respecto a la palabra clave "this" en frente del campo y los métodos en c #? , pero estoy preguntando por m_nothis.

PPS También vea ¿Por qué Microsoft hizo que los parámetros, las variables locales y los campos privados tengan la misma convención de nomenclatura de nombres?

Betty Crokker
fuente
99
¿C # no tiene la thispalabra clave? ¿No podría referirse a los miembros que usan this, como this.count?
FrustratedWithFormsDesigner
3
El prefijo m_ es un isma C ++ donde evita conflictos de nombres entre campos y métodos. En C # esto no es un problema porque los nombres de los métodos deben comenzar con mayúsculas, los campos con minúsculas.
amon
44
Si está imprimiendo código en 2017, está haciendo algo mal. En los otros dos escenarios, debería ser bastante obvio que countno apareció de la nada, ya que no se declaró en el método. Francamente, el argumento de "fuera de IDE" es REALMENTE débil. Especialmente porque incluso hay herramientas de revisión de código que funcionan en el IDE.
Andy
44
Todavía publicación relevante de Joel .
Kasey Speakman

Respuestas:

19

Cuando estaban trabajando en la API para Windows, Microsoft recomendó el uso de la notación húngara, usando 'm_', así como 'i', 'sz', etc. En ese momento, esto fue útil porque el IDE no era robusto suficiente para mostrarle esta información.

C #, por otro lado, tiene un IDE muy robusto desde el principio.

Entonces hicieron la recomendación en las Pautas de nomenclatura de Microsoft para C # para campos públicos estáticos o protegidos:

 DO use PascalCasing in field names.
 DO name fields using a noun, noun phrase, or adjective.
X DO NOT use a prefix for field names.

Ahora que puede desplazarse sobre él con el mouse o presionar CTRL + K + W y obtener toda la información sobre el miembro, Microsoft llegó a la determinación de que los prefijos son incorrectos; Son una solución manual a un problema ya resuelto.

Los campos privados están específicamente excluidos de las pautas, pero Microsoft parece haber llegado a la conclusión de que este es el caso incluso para los campos privados que avanzan internamente, lo que puede ver si apunta Resharper a .NET 4.5 o .NET Core.

Usa tus herramientas, no lo hagas manualmente.

Kevin Fee
fuente
2
La misma respuesta que a Andy ... sí, pero ... hay muchas situaciones en las que estoy mirando código fuera del IDE. Ejemplos: (1) herramientas de fusión. (2) herramientas de revisión de código. (3) impresiones (jadeo).
Betty Crokker
Andy publicó literalmente en el mismo instante que lo hice. Vi el mensaje emergente "1 comentario nuevo" cuando hice clic en "agregar respuesta". En cuanto a esas herramientas, dos de ellas ya están en el IDE. Impresiones ... ¿por qué haces eso?
Kevin Fee
1
Mi pregunta inicial sigue sin respuesta ... sí, Microsoft dijo que no use m_ y los fabricantes de herramientas siguieron eso, pero esa no es una razón técnica para usar m_, es lo que a menudo se llama "apelación a la autoridad". La única razón técnica que he visto para no usar m_ provino de Timothy Truckle y no lo entiendo ...
Betty Crokker
8
@BettyCrokker Porque m_ no agrega absolutamente ningún valor. El IDE le dice miembro o no, por lo que m_ es redundante. ¿Por qué estás obsesionado con m_? ¿Por qué no l_ (para local)? ¿Por qué no afsdfjdskfjas_? Voy a rechazar la pregunta, porque de todos modos es un argumento religioso tonto. Haz lo que quieras. Las directrices que está golpeando también dicen que "las directrices de nombres de campo se aplican a campos estáticos públicos y protegidos . Interna y campos privados están _No_ cubiertos por las directrices , y los campos de instancia públicas o protegidas no están permitidos por las directrices de diseño de miembro."
Andy
1
@BettyCrokker OK, pero está preguntando acerca de las pautas que se hacen con esa suposición. También se queja de las herramientas estáticas que probablemente también utilizan esa suposición, porque "nadie" (dentro de un margen de error estadístico aceptable) imprime su código C #. No sé qué herramientas de análisis estadístico utiliza, pero Resharper es bastante específico: los campos privados son "_lowerCase". Los miembros públicos de cualquier tipo o "UpperCase" y los locales son "lowerCase". Ahorrar en la "m" en realidad se ve muy bien, de todos modos.
Kevin Fee
17

C # no tiene ninguna variable global, por lo que solo deja variables locales y campos de clase.

Y si tiene tantas variables locales que pierde la noción de si un identificador es uno, seguramente ha violado una de las pautas para gestionar la complejidad de manera más grave.

Intente extraer un objeto de parámetro, intente dividir su función en múltiples más simples, seguramente debería refactorizar su código a menos que le guste ahogarse en minucias y complejidad, así como no tener en cuenta la mantenibilidad.

Ahora que hemos establecido que el propósito de las decoraciones de "esto es un miembro" ya no se cumple, ya que el alcance de la función debería ser demasiado pequeño y el alcance global no existe, hay una desventaja en esos marcadores: inhiben mover argumentos / variables locales a miembros, o al revés.

Deduplicador
fuente
Una vez que ingresas, también podría ser una clase o un espacio de nombres.
CodesInChaos
3
No creo que esto realmente responda la pregunta.
Vladimir Stokic
55
@FrankHileman En realidad, lo es. Explica por qué no hay una buena razón para uglificar un nombre.
Deduplicador
2
¿"feo"? Si este es el caso, es una pregunta de solo opinión. Todos pueden tener una opinión diferente sobre la fealdad. Hay razones para preferir una convención sobre otra, pero no hay una convención estándar en este caso.
Frank Hileman
1
La belleza de @Dupuplicator está en el ojo del espectador. Convenciones que una vez consideré feas, ahora aprecio que sean útiles.
Frank Hileman
11

¿Por qué los desarrolladores evitan anteponer espacios de nombres con la directiva using namespace?

System.DateTime()es mucho más explícita que DateTime(). Me dice exactamente con qué estoy tratando. ¿Es solo porque somos mecanógrafos perezosos?

No. Somos mecanógrafos perezosos, pero esa no es la razón.

Preferimos los estilos de codificación que nos permiten ignorar los detalles y centrarnos en las abstracciones. Forzar nombres para comunicar detalles de la estructura es una distracción. Cuando estoy trabajando en un algoritmo complicado, no quiero nombres que insistan en recordarme cada pequeño detalle sobre la cosa con la que estoy trabajando. Solo necesito el nombre para evitar que me confunda Timery DateTime. Me preocuparé si son compatibles en otro lugar.

Es la misma razón por la que no quieres dos hombres en tu equipo llamados Jon. El hecho de que tengan apellidos no es una razón para darles prefijos o sufijos. Solo voy a llamarte Skeet. Cualquier cosa más es un dolor rotundo.

naranja confitada
fuente
2
Esto no parece responder la pregunta. No hay preferencia general o convención para campos privados en .net.
Frank Hileman
12
@FrankHileman En general, preferimos buenos nombres. Las convenciones no crean buenos nombres. Las convenciones crean nombres como McOhPlease Von StopItAlreadyStine.
candied_orange
Las convenciones son solo para facilitar el trabajo con diferentes códigos de desarrollador. Su respuesta se dirige al húngaro, pero no existe una convención para los campos privados, por lo que la pregunta se basa en una suposición falsa.
Frank Hileman
7

Vamos a expandirnos un poco.

Hay 3 estilos de prefijos comunes, todos los cuales se encuentran ocasionalmente en el código de C #:

  • metro_
  • _ _
  • esta.

Tales prefijos se usan comúnmente en la implementación privada de una clase . La recomendación de Microsoft se refiere principalmente a las partes expuestas de una clase.

La ventaja de usar m_over _es que el prefijo puede ser el mismo en toda la base de código si usa c #, así como los lenguajes donde _no se permite usar como prefijo . * La ventaja de m_over this.es que es más corto y que no lo hará accidentalmente Olvídate del prefijo.

La ventaja de _over m_es que es más corto y que todo lo que comienza con el prefijo se ordena en la parte superior se ordena alfabéticamente por una herramienta. La ventaja de _Over this.es que es más corto y que no te olvidarás accidentalmente del prefijo.

La ventaja de this.over m_y _es que puede usarlo en una base de código donde _o m_no es una convención universal y aceptada. Eso también significa que las necesidades de nadie saber sobre el _o m_convención, que puede ser visto como hacer las cosas más simples. Como inconveniente, debido a que al compilador no le importa este prefijo, this.ocasionalmente faltará el prefijo y, como tal, no será tan confiable como un indicador.


* Una vez que comience a acceder a las clases de C # que usan _C ++ / CLI (que realmente no debería usar _), notará que acordar un prefijo común es más complejo de lo que debería ser. Decidir no tener un prefijo elimina ese ruido. Combine esto con la respuesta de Deduplicator, que parafrasearé como "la ventaja de un prefijo es casi insignificante", y nos da: "Hay un pequeño problema al usar prefijos, y una pequeña ventaja, por lo que es una opción válida para no usalos, usalos a ellos".


Hay una pregunta anterior sobre stackoverflow relacionada con esto.

Peter - Unban Robert Harvey
fuente
1
Buena comparación Sin embargo, encuentro que no usar ningún prefijo funciona bien en la práctica.
Phil1970
6

Cabe señalar que la guía de estilo de MS solo habla sobre métodos y variables públicas y protegidas. es decir, los otros desarrolladores verán si usan tu biblioteca.

La idea es que las bibliotecas de Microsoft tengan un aspecto estándar para los desarrolladores que las consuman.

Puede usar el estilo que desee en sus variables privadas o locales.

Habiendo dicho eso, los prefijos variables se desaconsejan en general por varias razones

  • Pueden estar equivocados y, por lo tanto, ser engañosos.
  • Si está prefijando por tipo de variable, no está claro cómo maneja las clases que ha creado.
  • Hay una serie de convenciones y no siempre están de acuerdo. Lo que encuentres útil, otro desarrollador puede encontrar inútil
  • En el caso de las variables miembro, puede usar este 'esto'. palabra clave para indicar el alcance y el cumplidor lo hará cumplir.
Ewan
fuente
2
Originalmente no utilicé ningún prefijo para los campos. Más tarde, cambié a usar un solo carácter "_", ya que vi a otros usarlo porque mueve todos los campos a la parte superior de la lista alfabética de miembros cuando usa cuadros de lista desplegable IDE. La falta de una convención estándar puede ser molesta al leer el código escrito por muchos desarrolladores diferentes.
Frank Hileman
después de un tiempo ves tantos estilos diferentes que simplemente los ignoras. Si intentaras imponer un estilo,
Ewan
44
Si aplica "Pueden estar equivocados y, por lo tanto, son engañosos" a casi cualquier cosa, descubrirá rápidamente que no debe nombrar variables o funciones, ¡después de todo, podrían estar equivocadas! ¿Y supongo que se supone que su segundo punto dice "clases que NO ha creado"? O simplemente no lo entiendo ... En cualquier caso, está empezando a parecer que tener un solo prefijo de subrayado para campos no públicos es un estándar no oficial, esa es probablemente la dirección que tomaremos.
Betty Crokker
el compilador verifica algunas cosas, por lo que m_count podría no ser una variable miembro, pero this.count siempre lo será
Ewan
mucha gente usa _ pero las cosas cambian con el tiempo, encontrará que el código escrito para 'mejores prácticas' el año pasado no coincide con las convenciones de este año
Ewan
4

Para mí, si estoy leyendo el código de otra persona y veo esto:

count = 3;

Supongo que 'contar' es una variable local de esa función y estoy haciendo algo que se usará en otra parte de la función

Y tendrá toda la razón si está leyendo el código fuente que respeta las convenciones de estilo de C #. En tal código:

this.count = 3;

asigna un valor a un campo.

count = 3;

asigna un valor a una variable local.

Por lo tanto, las convenciones de estilo de C # son claras e inequívocas. Te obligan a no usar ningún prefijo simplemente porque no necesitas uno.

En cuanto al código fuente para que los autores decidieron utilizar sus convenciones personales en su lugar, usted debe preguntar a ellos personalmente por qué tomó la decisión de hacerlo. Si no usan prefijos tales como guiones bajos, mientras que no usan this.ninguno, conduciría efectivamente no solo a un código difícil de leer, sino también a casos en los que se necesitaría una convención adicional:

public class Hello
{
    private string greetings;

    public Hello(string greetings)
    {
        greetings = greetings; // Wait, what is that?!
    }
}

¿C # no tiene esta palabra clave? ¿No podría referirse a los miembros que usan esto, como this.count?

Sí, pero (1) es más mecanografía y (2) es fácil de olvidar. Si el campo se llama m_count, no tengo que recordar escribir esto.m_count

Aquí, sin embargo, te equivocas.

Es más escribiendo cuando escribe su código en el Bloc de notas. Con un IDE con autocompletado o, mejor, IntelliSense, la comparación entre this.county m_countno es tan obvia. Tenga en cuenta el Shift+ -requerido para escribir el guión bajo.

En cuanto a "fácil de olvidar", nuevamente, esto es relevante solo para los usuarios de Notepad. No solo StyleCop y Resharper no te permitirán olvidar ponerlo this.cuando sea necesario, sino que después de unas horas de programación, ponerlo this.cuando sea necesario se convierte en un hábito.

Arseni Mourzenko
fuente
66
Me parece que usarlo thissolo cuando lo necesitas da como resultado una sensación realmente inconsistente y me distrae con demasiada frecuencia. Si va a usar en thislugar de un prefijo, creo que siempre debe usarlo. En cuyo caso, se convierte en un prefijo y también podría usarlo _.
RubberDuck
1
RubberDuck tiene razón. "esta." es un prefijo, aunque tiene significado de compilador.
Frank Hileman
4

El prefijo "miembro" es una especie de detalle de implementación. Desvía su atención del dominio del problema al dominio de la solución técnica que sesga su mente al pensar en posibles refactorizaciones. - yo

¿Puedes explicar más? La suya es la única razón por la que he visto por qué no usar el prefijo y no lo entiendo ... (lo que quiero decir, las otras cosas que la gente dice son razones por las que no tengo que usarlo, que no es lo mismo que por qué no debería) - Betty Crokker

Mi punto es extender las respuestas de @CandiedOrange y @Ewan.

Los prefijos dificultan la refactorización

A medida que su código evoluciona, las variables y los métodos pueden cambiar su alcance. P.ej. puede crear un método privado y luego descubrir que también debería estar disponible para otras (nuevas) clases. Similar puede suceder con los parámetros del método y las variables locales.

Supongamos que crea una calculadora de impuestos. De acuerdo con el principio del alcance de visibilidad del arrendamiento para las variables, comienza con un método que toma tanto la tasa impositiva como el valor base como parámetros:
(el ejemplo puede parecer Java-ish ...)

class TaxCalculator{
   double Calculate(double p_TaxRateInpercent, double p_BaseValue){
     return (100+p_TaxRateInpercent)/100 * p_BaseValue;
   }
} 

a continuación, debe implementar la operación inversa y, según TDD, lo hace con el menor esfuerzo:

class TaxCalculator{
   double Calculate(double p_TaxRateInpercent, double p_BaseValue){
     return (100+p_TaxRateInpercent)/100 * p_BaseValue;
   }
   double CalculateBase(double p_TaxRateInpercent, double p_TaxedValue){
     return p_TaxedValue/((100+p_TaxRateInpercent)/100);
   }
} 

El siguiente TDD quiere que refactorice el código para que sea más limpio, lo que es reducir la lista de parámetros de ambos métodos.
(Sí, primero extraería el cálculo duplicado, pero permítame entender mi punto ...)
La solución obvia es cambiar la tasa impositiva en una variable miembro pasándola como un parámetro constructor.

class TaxCalculator{
   double m_TaxRateInpercent;
   TaxCalculator(double p_TaxRateInpercent){
     m_TaxRateInpercent = p_TaxRateInpercent;
   }
   double Calculate(double p_BaseValue){
     return (100+m_TaxRateInpercent)/100 * p_BaseValue;
   }
   double CalculateBase(double p_TaxedValue){
     return p_TaxedValue/((100+m_TaxRateInpercent)/100);
   }
} 

Como puede ver, tuvimos que cambiar todas las líneas de nuestros métodos existentes (cualquier línea que solíamos p_TaxRateInpercentser exactas).

El problema es que no tiene soporte de su IDE para cambiar el nombre en toda la clase. La única ayuda es buscar / reemplazar, que también cambiará el constructor o cualquier parte que accidentalmente contenga la cadena p_TaxRateInpercent.
Su IDE podría ofrecer un cambio a ... refactorizar nombres de variables indefinidos, pero esto puede estar restringido al alcance del método

Sin el prefijo, solo las firmas del método habrían cambiado. No se habría necesitado ningún cambio de nombre.


Prefija el historial de desorden SCM cuando se cambia

Además, el SCM registra el cambio del prefijo como cambios en la lógica, ¡aunque la lógica no cambió! La historia de SCM está abarrotada con este cambio técnico que oculta lo que es importante y aumenta el riesgo de conflictos con los cambios (lógicos reales) de otros.

Timothy Truckle
fuente
1

Yo uso el prefijo _ para todas las variables miembro. Odio el prefijo 'this' porque, aunque algunos afirman que es la forma correcta de hacer las cosas, agrega mucho ruido / desorden a su base de código. Un solo guión bajo también es una práctica común en las muestras de Microsoft.

Entonces, en su ejemplo, tendría:

int _count = 10;
Eterno21
fuente