valor - variables de referencia en c#
NĂºmeros nombrados como variables (10)
Recientemente lo he visto un par de veces en el código de perfil alto , donde los valores constantes se definen como variables, se nombran después del valor y luego se usan solo una vez. Me pregunté por qué se hace?
Ej. Fuente Linux (resize.c)
unsigned five = 5;
unsigned seven = 7;
Por ejemplo, C # .NET Source (Quaternion.cs)
double zero = 0;
double one = 1;
Variables bien nombradas
Dar nombres propios a las variables puede aclarar drásticamente el código, como
constant int MAXIMUM_PRESSURE_VALUE=2;
Esto da dos ventajas clave:
El valor
MAXIMUM_PRESSURE_VALUE
se puede usar en muchos lugares diferentes, si por alguna razón ese valor cambia, necesita cambiarlo en un solo lugar.Cuando se usa, muestra inmediatamente lo que hace la función, por ejemplo, el siguiente código obviamente comprueba si la presión es peligrosamente alta:
if (pressure>MAXIMUM_PRESSURE_VALUE){ //without me telling you you can guess there''ll be some safety protection in here }
Variables mal nombradas
Sin embargo, todo tiene un argumento contrario y lo que has mostrado se parece mucho a una buena idea tomada hasta el momento que no tiene sentido. Definir TWO
como 2 no agrega ningún valor
constant int TWO=2;
- El valor
TWO
se puede usar en muchos lugares diferentes, tal vez para duplicar cosas, tal vez para acceder a un índice. Si en el futuro necesita cambiar el índice, no puede simplemente cambiar aint TWO=3;
porque eso afectaría a todas las otras formas (completamente no relacionadas) que has usado DOS, ahora estarías triplicando en lugar de duplicar, etc. Donde se usa, no le da más información que si solo usó "2". Compara los siguientes dos códigos:
if (pressure>2){ //2 might be good, I have no idea what happens here }
o
if (pressure>TWO){ //TWO means 2, 2 might be good, I still have no idea what happens here }
Peor aún (como parece ser el caso aquí)
TWO
puede que no sea igual a 2, si es así, esta es una forma de ofuscación donde la intención es hacer que el código sea menos claro: obviamente lo logra.
La razón habitual de esto es un estándar de codificación que prohíbe los números mágicos pero no cuenta TWO
como un número mágico; ¡Cual, por supuesto, es! El 99% del tiempo que desea usar un nombre de variable significativo, pero en ese 1% del tiempo, usar TWO
lugar de 2
no le otorga nada (lo siento, quiero decir ZERO
).
Este código está inspirado en Java, pero está pensado para ser agnóstico del lenguaje.
¿Cómo se llega a la conclusión de que se usa solo una vez? Es público, se puede utilizar cualquier número de veces desde cualquier ensamblaje.
public static readonly Quaternion Zero = new Quaternion();
public static readonly Quaternion One = new Quaternion(1.0f, 1.0f, 1.0f, 1.0f);
Lo mismo se aplica a la clase decimal
.Net framework. Lo que también expone constantes públicas como esta.
public const decimal One = 1m;
public const decimal Zero = 0m;
He usado algo parecido a eso un par de veces para escribir valores en archivos:
const int32_t zero = 0 ;
fwrite( &zero, sizeof(zero), 1, myfile );
fwrite acepta un puntero de const, pero si alguna función necesita un puntero de no const, terminarás usando una variable de no const.
PD: Eso siempre me hace preguntarme cuál puede ser el tamaño de cero.
Los números 0, 1, ... son enteros. Aquí, las ''variables nombradas'' le dan al entero un tipo diferente. Puede ser más razonable especificar estas constantes (const unsigned five = 5;)
Los números a menudo reciben un nombre cuando estos números tienen un significado especial.
Por ejemplo, en el caso del cuaternión, el cuaternión de identidad y el cuaternión de longitud unitaria tienen un significado especial y se utilizan con frecuencia en un contexto especial. A saber, Quaternion con (0,0,0,1) es un cuaternión de identidad, por lo que es una práctica común definirlos en lugar de usar números mágicos.
Por ejemplo
// define as static
static Quaternion Identity = new Quaternion(0,0,0,1);
Quaternion Q1 = Quaternion.Identity;
//or
if ( Q1.Length == Unit ) // not considering floating point error
Mi organización tiene ciertas pautas de programación, una de las cuales es el uso de números mágicos ...
p.ej:
if (input == 3) //3 what? Elephants?....3 really is the magic number here...
Esto se cambiaría a:
#define INPUT_1_VOLTAGE_THRESHOLD 3u
if (input == INPUT_1_VOLTAGE_THRESHOLD) //Not elephants :(
También tenemos un archivo fuente con -200,000 -> 200,000 # definido en el formato:
#define MINUS_TWO_ZERO_ZERO_ZERO_ZERO_ZERO -200000
que se puede usar en lugar de números mágicos, por ejemplo, cuando se hace referencia a un índice específico de una matriz.
Me imagino que esto se ha hecho por "legibilidad".
Nombrar números es una práctica terrible, un día algo tendrá que cambiar y terminará con unsigned five = 7
.
Si tiene algún significado, dale un nombre significativo. El ''número mágico'' five
no es una mejora sobre el número mágico 5
, es peor porque podría no ser igual a 5
.
Este tipo de cosas generalmente surge de algunas pautas de estilo de programación de estilo de culto de la carga donde alguien escuchó que "los números mágicos son malos" y prohibió su uso sin entender completamente por qué.
Uno de mis primeros trabajos de programación fue en un PDP 11 usando Basic. El intérprete básico asignó la memoria a cada número requerido, por lo que cada vez que el programa menciona 0, se utilizará un byte o dos para almacenar el número 0. Por supuesto, en aquellos días la memoria era mucho más limitada que la actual y, por lo tanto, era importante para conservar.
Cada programa en ese lugar de trabajo comenzó con:
10 U0%=0
20 U1%=1
Es decir, para aquellos que han olvidado su Basic:
Line number 10: create an integer variable called U0 and assign it the number 0
Line number 20: create an integer variable called U1 and assign it the number 1
Estas variables, por convención local, nunca tuvieron ningún otro valor, por lo que fueron efectivamente constantes. Permitieron el uso de 0 y 1 en todo el programa sin perder memoria.
Aaaaah, los buenos viejos tiempos!
Version corta:
- Una constante
five
que solo tiene el número cinco es bastante inútil. No haga esto por ninguna razón (aunque a veces tiene que hacerlo debido a la sintaxis o las reglas de escritura). - Las variables nombradas en Quaternion.cs no son estrictamente necesarias, pero puede hacer que el código sea mucho más legible con ellas que sin ellas.
- Las variables nombradas en ext4 / resize.c no son constantes en absoluto. Son contadores de nombre lacónico. Sus nombres ocultan un poco su función, pero este código realmente sigue correctamente los estándares de codificación especializados del proyecto.
¿Qué está pasando con Quaternion.cs?
Este es bastante fácil.
Justo después de esto:
double zero = 0;
double one = 1;
El código hace esto:
return zero.GetHashCode() ^ one.GetHashCode();
Sin las variables locales, ¿qué aspecto tiene la alternativa?
return 0.0.GetHashCode() ^ 1.0.GetHashCode(); // doubles, not ints!
¡Que desastre! La legibilidad está definitivamente del lado de la creación de los locales aquí. Además, creo que nombrar explícitamente las variables indica "Hemos pensado en esto con cuidado" mucho más claramente que solo escribir una declaración de retorno confusa.
¿Qué está pasando con resize.c?
En el caso de ext4 / resize.c, estos números no son en realidad constantes. Si sigues el código, verás que sus contadores y sus valores realmente cambian en múltiples iteraciones de un bucle while.
unsigned three = 1;
unsigned five = 5;
unsigned seven = 7;
Tres es igual a uno, ¿eh? ¿De qué trata eso?
Vea, lo que realmente sucede es que update_backups
pasa estas variables por referencia a la función ext4_list_backups
:
/*
* Iterate through the groups which hold BACKUP superblock/GDT copies in an
* ext4 filesystem. The counters should be initialized to 1, 5, and 7 before
* calling this for the first time. In a sparse filesystem it will be the
* sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
* For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
*/
static unsigned ext4_list_backups(struct super_block *sb, unsigned *three,
unsigned *five, unsigned *seven)
Son contadores que se conservan a lo largo de múltiples llamadas. Si ext4_list_backups , verá que está haciendo malabares con los contadores para encontrar la siguiente potencia de 3, 5 o 7 , creando la secuencia que ve en el comentario: 1, 3, 5, 7, 9, 25 , 27, & c.
Ahora, para la parte más extraña: la variable three
se inicializa a 1 porque 3 0 = 1. La potencia 0 es un caso especial, porque es la única vez que 3 x = 5 x = 7 x . Intente volver a escribir ext4_list_backups
para trabajar con los tres contadores inicializados en 1 (3 0 , 5 0 , 7 0 ) y verá cuánto más incómodo es el código. A veces es más fácil simplemente decirle a la persona que llama que haga algo funky (inicialice la lista a 1, 5, 7) en los comentarios.
Entonces, ¿ five = 5
buen estilo de codificación?
¿Es "cinco" un buen nombre para lo que representa la variable five
en resize.c? En mi opinión, no es un estilo que deba emular en cualquier proyecto al azar que asuma. El simple nombre five
no comunica mucho sobre el propósito de la variable. Si está trabajando en una aplicación web o está creando rápidamente un prototipo de un cliente de chat de video o algo y decide nombrar una variable five
, probablemente creará dolores de cabeza y molestias para cualquier persona que necesite mantener y modificar su código.
Sin embargo, este es un ejemplo en el que las generalidades sobre la programación no pintan el cuadro completo . Eche un vistazo al documento de estilo de codificación del kernel , en particular el capítulo sobre nombres.
Las variables GLOBALES (para usar solo si realmente las necesita) deben tener nombres descriptivos, al igual que las funciones globales. Si tiene una función que cuenta el número de usuarios activos, debe llamar a eso "count_active_users ()" o similar, no debe llamarlo "cntusr ()".
...
Los nombres de las variables LOCALES deben ser cortos, y al punto. Si tiene algún contador aleatorio de bucles enteros, probablemente debería llamarse "i". Llamarlo "loop_counter" no es productivo, si no hay posibilidad de que sea mal entendido. De manera similar, "tmp" puede ser casi cualquier tipo de variable que se usa para mantener un valor temporal.
Si tiene miedo de mezclar sus nombres de variables locales, tiene otro problema, que se denomina síndrome de desequilibrio hormonal de la función-crecimiento. Ver capítulo 6 (Funciones).
Parte de esto es la tradición de codificación de estilo C Parte de ello es la ingeniería social intencional. Una gran cantidad de código del kernel es material sensible, y ha sido revisado y probado muchas veces. Dado que Linux es un gran proyecto de código abierto, no está realmente perjudicando las contribuciones; en la mayoría de los casos, el mayor desafío es verificar la calidad de esas contribuciones.
Llamar a esa variable five
lugar de algo como nextPowerOfFive
es una manera de desalentar a los contribuyentes de entrometerse en un código que no entienden. Es un intento de obligarte a leer realmente el código que estás modificando en detalle, línea por línea, antes de intentar realizar cambios.
¿Tomaron los mantenedores del núcleo la decisión correcta? No puedo decir Pero es claramente un movimiento intencional.
algunas veces es más legible escribir:
double pi=3.14; //Constant or even not constant
...
CircleArea=pi*r*r;
en lugar de:
CircleArea=3.14*r*r;
y podría ser que pi
más pi
nuevo (no estás seguro, pero crees que es posible más adelante o en otras clases si son públicas)
y luego, si quieres cambiar pi=3.14
en pi=3.141596
es más fácil.
y algunos otros como e=2.71
, Avogadro
y etc.