Recientemente pensé en el uso de enteros sin signo en C # (y supongo que se puede decir un argumento similar sobre otros "lenguajes de alto nivel")
Cuando necesito un número entero, normalmente no me enfrento al dilema del tamaño de un número entero, un ejemplo sería una propiedad de edad de una clase Persona (pero la pregunta no se limita a las propiedades). Con eso en mente, hasta donde puedo ver, solo hay una ventaja de usar un entero sin signo ("uint") sobre un entero con signo ("int"): legibilidad. Si deseo expresar la idea de que una edad solo puede ser positiva, puedo lograrlo estableciendo el tipo de edad en uint.
Por otro lado, los cálculos en enteros sin signo pueden conducir a errores de todo tipo y dificulta la realización de operaciones como restar dos edades. (Leí que esta es una de las razones por las que Java omitió enteros sin signo)
En el caso de C #, también puedo pensar que una cláusula de protección en el setter sería una solución que ofrece lo mejor de dos mundos, pero esto no sería aplicable cuando, por ejemplo, una edad se pasaría a algún método. Una solución alternativa sería definir una clase llamada Age y hacer que la edad de la propiedad sea lo único allí, pero este patrón me haría crear muchas clases y sería una fuente de confusión (otros desarrolladores no sabrían cuándo un objeto es solo un contenedor y cuando es algo más sofisticado).
¿Cuáles son algunas de las mejores prácticas generales con respecto a este tema? ¿Cómo debo lidiar con este tipo de escenario?
fuente
Respuestas:
Los diseñadores de .NET Framework eligieron un número entero con signo de 32 bits como su "número de propósito general" por varias razones:
La razón para usar ints sin firmar no es la legibilidad; tiene la capacidad de obtener las matemáticas que solo proporciona un int sin firmar.
Las cláusulas de protección, la validación y las condiciones previas del contrato son formas perfectamente aceptables de asegurar rangos numéricos válidos. Rara vez, un rango numérico del mundo real corresponde exactamente a un número entre cero y 2 32 -1 (o cualquiera que sea el rango numérico nativo del tipo numérico que elija), por lo que usar un
uint
para restringir su contrato de interfaz a números positivos es una especie de no viene al caso.fuente
for (uint j=some_size-1; j >= 0; --j)
- chillidos ( ¡No estoy seguro si esto es un problema en C #)! Encontré este problema en el código anterior que intentaba usar unsigned int en el lado C tanto como sea posible, y terminamos cambiándolo para favorecerloint
más adelante, y nuestras vidas fueron mucho más fáciles con menos advertencias del compilador también.int
mayor parte del tiempo porque esa es la convención establecida, y es lo que la mayoría de la gente esperará que se use de manera rutinaria. Úselouint
cuando requiera las capacidades especiales de auint
". Recuerde, los diseñadores de Framework decidieron seguir esta convención ampliamente, por lo que ni siquiera puede usarlauint
en muchos contextos de Framework (no es compatible con el tipo).En general, siempre debe usar el tipo de datos más específico posible para sus datos.
Si, por ejemplo, está utilizando Entity Framework para extraer datos de una base de datos, EF usará automáticamente el tipo de datos más cercano al utilizado en la base de datos.
Hay dos problemas con esto en C #.
Primero, la mayoría de los desarrolladores de C # usan solo
int
para representar números enteros (a menos que haya una razón para usarlong
). Esto significa que otros desarrolladores no pensarán en verificar el tipo de datos, por lo que obtendrán los errores de desbordamiento mencionados anteriormente. El segundo, y más importante cuestión, es / fue que de .NET operadores aritméticos originales sólo se admiteint
,uint
,long
,ulong
,float
, doble, ydecimal
*. Este sigue siendo el caso hoy (consulte la sección 7.8.4 en la especificación del lenguaje C # 5.0 ). Puede probar esto usted mismo usando el siguiente código:El resultado de nuestro
byte
-byte
es unint
(System.Int32
).Estos dos problemas dieron lugar a la práctica de "solo usar int para números enteros", que es tan común.
Entonces, para responder su pregunta, en C # generalmente es una buena idea seguir a
int
menos que:byte
y anint
o anint
y along
es crítica, o las diferencias aritméticas de unsigned ya mencionadas).Si necesita hacer cálculos matemáticos con los datos, siga los tipos comunes.
Recuerda, puedes lanzar de un tipo a otro. Esto puede ser menos eficiente desde el punto de vista de la CPU, por lo que probablemente sea mejor con uno de los 7 tipos comunes, pero es una opción si es necesario.
Enumeraciones (
enum
) es una de mis excepciones personales a las pautas anteriores. Si solo tengo algunas opciones, especificaré que la enumeración sea un byte o un short. Si necesito ese último bit en una enumeración marcada, especificaré el tipo parauint
que pueda usar hexadecimal para establecer el valor de la bandera.Si utiliza una propiedad con código de restricción de valor, asegúrese de explicar en la etiqueta de resumen qué restricciones existen y por qué.
* Se usan alias C # en lugar de nombres .NET como,
System.Int32
ya que esta es una pregunta C #.Nota: hubo un blog o artículo de los desarrolladores de .NET (que no puedo encontrar), que señalaba el número limitado de funciones aritméticas y algunas razones por las que no se preocupaban por ello. Como recuerdo, indicaron que no tenían planes para agregar soporte para los otros tipos de datos.
Nota: Java no admite tipos de datos sin signo y anteriormente no tenía soporte para números enteros de 8 o 16 bits. Dado que muchos desarrolladores de C # provenían de un entorno Java o necesitaban trabajar en ambos idiomas, las limitaciones de un idioma a veces se impondrían artificialmente en el otro.
fuente
Principalmente debe tener en cuenta dos cosas: los datos que está representando y los pasos intermedios en sus cálculos.
Ciertamente tiene sentido tener edad
unsigned int
, porque generalmente no consideramos edades negativas. Pero luego mencionas restar una edad de otra. Si solo restamos ciegamente un número entero de otro, entonces definitivamente es posible terminar con un número negativo, incluso si previamente estuvimos de acuerdo en que las edades negativas no tienen sentido. Entonces, en este caso, desearía que su cálculo se haga con un entero con signo.En cuanto a si los valores sin signo son malos o no, diría que es una gran generalización decir que los valores sin signo son malos. Java no tiene valores sin signo, como mencionaste, y constantemente me molesta. A
byte
puede tener un valor de 0-255 o 0x00-0xFF. Pero si desea crear una instancia de un byte mayor que 127 (0x7F), debe escribirlo como un número negativo o convertir un entero en un byte. Terminas con un código que se ve así:Lo anterior me molesta sin fin. No se me permite que un byte tenga un valor de 197, a pesar de que es un valor perfectamente válido para la mayoría de las personas sensatas que se ocupan de bytes. Puedo emitir el entero o puedo encontrar el valor negativo (197 == -59 en este caso). Considere también esto:
Como puede ver, agregar dos bytes con valores válidos y terminar con un byte con un valor válido termina cambiando el signo. No solo eso, sino que no es inmediatamente obvio que 70 + 80 == -106. Técnicamente esto es un desbordamiento, pero en mi opinión (como ser humano) un byte no debería desbordarse para valores por debajo de 0xFF. Cuando hago aritmética de bits en papel, no considero que el octavo bit sea un bit de signo.
Trabajo con muchos enteros a nivel de bit, y tener todo firmado generalmente hace que todo sea menos intuitivo y más difícil de manejar, porque debes recordar que desplazar a la derecha un número negativo te da nuevos
1
s en tu número. Mientras que desplazar a la derecha un entero sin signo nunca hace eso. Por ejemplo:Simplemente agrega pasos adicionales que creo que no deberían ser necesarios.
Si bien lo utilicé
byte
anteriormente, lo mismo se aplica a los enteros de 32 bits y 64 bits. No tenerunsigned
es paralizante y me sorprende que haya lenguajes de alto nivel como Java que no los permiten en absoluto. Pero para la mayoría de las personas esto no es un problema, porque muchos programadores no se ocupan de la aritmética de nivel de bits.Al final, es útil usar enteros sin signo si los está pensando como bits, y es útil usar números enteros con signo cuando los piensa como números.
fuente