¿Debo usar uint en C # para valores que no pueden ser negativos?

80

Acabo de intentar implementar una clase donde se encuentran numerosas propiedades de longitud / recuento, etc.en uintlugar de int. Sin embargo, mientras lo hacía, noté que en realidad es doloroso hacerlo, como si nadie realmente quisiera hacer eso.

Casi todo lo que entrega un tipo integral devuelve un int, por lo que requiere lances en varios puntos. Quería construir un StringBuffercon su longitud de búfer predeterminada en uno de los campos de esa clase. También requiere un yeso.

Así que me pregunté si debería volver a intaquí. Ciertamente no estoy usando todo el rango de todos modos. Simplemente pensé que, dado que lo que estoy tratando allí simplemente no puede ser negativo (si lo fuera, sería un error), sería una buena idea usarlo uint.

PD: Vi esta pregunta y esto al menos explica por qué el marco en sí siempre se usa, intpero incluso en el propio código es realmente complicado de cumplir, uintlo que me hace pensar que aparentemente no es realmente deseado.

Joey
fuente

Respuestas:

42

Aunque estrictamente debe usar uintpara variables que contienen números enteros no negativos, se ha encontrado con una de las razones por las que no siempre es factible.

En este caso, no creo que valga la pena la reducción en la legibilidad que conlleva tener que hacer lanzamientos.

ChrisF
fuente
2
Es difícil de decir, no creo que los casts reduzcan mucho la legibilidad solo para los indexadores y puedes crear un contenedor. Y las entradas que se vuelven negativas son errores dolorosos y costosos.
user1496062
6
Si tiene que crear un contenedor para un int, entonces tiene más que problemas de legibilidad :)
S ..
61

Agregaré a las otras respuestas también que usar uint como tipo de campo público, propiedad, método, parámetro, etc., es una violación de las reglas de Common Language Specification y debe evitarse cuando sea posible.

Eric Lippert
fuente
3
Y me quemó Microsoft siguiendo estrictamente eso. Resulta que IntPtr debería haber tenido un constructor uint.
Joshua
3
También agregaría que si admitieran números naturales, por ejemplo, este tipo tiene un valor de 0 a 6, podrían eliminar todas las comprobaciones de límites de matriz, un aumento masivo del rendimiento del 5-10%.
user1496062
¿Está de acuerdo con la declaración anterior, Sr. Lippert?
AgentFire
@AgentFire: La declaración afirma que si alguien hiciera algo, eso causaría una mejora específica del rendimiento. No tengo capacidad para evaluar afirmaciones hipotéticas; si quieres saber si esa afirmación es cierta, pregúntale al usuario1496062 qué justifica su afirmación, no a mí; No soy yo quien hace la afirmación sin fundamento.
Eric Lippert
1
@EricLippert es uno de los "no sé" más cortos y largos, ¿no ?: p
AgentFire
4

Un valor negativo se usa a menudo para señalar una condición de error, y el tamaño de una operación a menudo es devuelto por una llamada de función; por tanto, un valor negativo puede indicar un error sin recurrir a un mecanismo de excepción.

También tenga en cuenta que .NET a menudo se basa en bibliotecas C directas, por lo que es sensato continuar con esta convención. Si necesita un espacio de índice más grande, puede romper la convención para diferentes mecanismos de señalización de errores.

Hassan Syed
fuente
24
.NET es bastante consistente con lanzar excepciones en lugar de devolver códigos de error. El retorno -1 no es algo común en el código administrado.
ChrisV
está de acuerdo, pero estamos hablando de una convención de diseño que prevalece en todas las tecnologías de Windows (además de las excepciones en las que son compatibles): aquellas partes de la biblioteca .NET que interactúan con código de bajo nivel tienen que traducirse entre códigos de error CRT y excepciones - es comprensible por qué presentan la convención de int's en lugar de uint's.
Hassan Syed
@HassanSyed aún, si algún winapi devuelve un valor negativo, el marco aún lanzará su propia excepción, envolviendo todo en un shell ordenado, por lo que ese argumento no se aplica aquí.
AgentFire
3

Mi sentimiento personal es que probablemente debería limitarse a int. No vale la pena agregar una conversión a casi todos los accesos a propiedades solo para recuperar un rango numérico que es poco probable que .NET le permita usar de todos modos.

ChrisV
fuente
3

El uso de un int también es útil para detectar el desbordamiento de enteros en las operaciones.

Ioan
fuente
No es 100% exacto, si se desborda tanto que llega a cero. Nuevamente, hay una protección contra desbordamiento habilitada de forma predeterminada.
AgentFire
3

En mi opinión, el inconveniente de usar uintes que oculta las condiciones de error. Los equivalentes del siguiente código no son tan agradables:

if (len < 0)
    terminate_program("length attained impossible value.");

Por supuesto, sus programas no deben calcular mal nada para empezar, pero creo que también deben escribirse para detectar rápidamente errores numéricos sin propagación. En el caso de que un MaxValue de 2 ^ 31 sea suficiente, digo uso intjunto con el uso adecuado System.Diagnostics.Debug.Assert()y las correspondientes verificaciones de errores como se ejemplificó anteriormente.

Si lo usa, uintúselo junto con checkedpara evitar el desbordamiento y obtener los mismos resultados. Sin embargo, he descubierto que la verificación es un poco difícil de aplicar al código existente que usa conversiones para algún propósito.

Heath Hunnicutt
fuente
2

No nade contra la corriente si no es necesario. No escribir su código con casts hace que su código sea más legible. Además, si sus posibles valores encajan dentro de un int, entonces usar un int no es un problema.

Si tiene miedo de desbordar un int, entonces por supuesto ... pero no optimice prematuramente.

Yo diría que la legibilidad mejorada de minimizar los lanzamientos supera el riesgo ligeramente elevado de un error con el uso de int.

Erik Funkenbusch
fuente
2

Si desea verificar que un valor sea positivo, probablemente una mejor manera sea simplemente usando assert (tenga en cuenta que esto es solo una técnica de depuración; debe asegurarse de que esto nunca ocurra en el código final).

using System.Diagnostics;
...
Debug.Assert (i > 0);
ternaryOperator
fuente
1
Consulte también el ArgumentOutOfRangeException.
Fred