Recentemente, pensei no uso de números inteiros não assinados em C # (e acho que argumento semelhante pode ser dito sobre outras "linguagens de alto nível")
Quando Na necessidade de um número inteiro, normalmente não sou confrontado com o dilema do tamanho de um número inteiro, um exemplo seria uma propriedade de idade de uma classe Person (mas a questão não se limita às propriedades). Com isso em mente, há, até onde posso ver, apenas uma vantagem de usar um número inteiro não assinado ("uint") sobre um número inteiro assinado ("int") - legibilidade. Se eu quiser expressar a ideia de que uma idade só pode ser positiva, posso conseguir isso definindo o tipo de idade como uint.
Por outro lado, cálculos em números inteiros não assinados podem levar a todos os tipos de erros e dificulta a execução de operações como subtração de duas idades. (Li que este é um dos motivos pelos quais o Java omitiu números inteiros não assinados)
No caso do C #, também posso pensar que uma cláusula de guarda no levantador seria uma solução que oferece o melhor dos dois mundos, mas isso não seria aplicável quando, por exemplo, uma idade seria passada para algum método. Uma solução alternativa seria definir uma classe chamada Age e ter a propriedade age como a única coisa lá, mas esse padrão faria Eu criar muitas classes e seria uma fonte de confusão (outros desenvolvedores não saberiam quando um objeto é apenas um invólucro e quando é algo mais sofisticado).
Quais são algumas das melhores práticas gerais em relação a esse problema? Como devo lidar com esse tipo de cenário?
fonte
Respostas:
Os designers do .NET Framework escolheram um número inteiro assinado de 32 bits como seu "número de uso geral" por vários motivos:
O motivo para usar entradas não assinadas não é a legibilidade; está tendo a capacidade de obter a matemática que somente um int não assinado fornece.
Cláusulas de guarda, validação e pré-condições do contrato são formas perfeitamente aceitáveis de garantir intervalos numéricos válidos. Raramente um intervalo numérico do mundo real corresponde exatamente a um número entre zero e 2 32 -1 (ou qualquer que seja o intervalo numérico nativo do tipo numérico que você escolheu), portanto, usar a
uint
para restringir seu contrato de interface a números positivos é uma espécie de além do ponto.fonte
for (uint j=some_size-1; j >= 0; --j)
- whoops ( não tenho certeza se esse é um problema em C #)! Eu encontrei esse problema no código antes, no qual tentamos usar int não assinado no lado C, tanto quanto possível - e acabamos mudando para favorecerint
mais tarde, e nossas vidas eram muito mais fáceis com menos avisos do compilador.int
maior parte do tempo, porque essa é a convenção estabelecida, e é o que a maioria das pessoas espera que seja usada rotineiramente. Useuint
quando você precisar dos recursos especiais de auint
". Lembre-se de que os designers do Framework decidiram seguir essa convenção extensivamente, então você não pode nem usá-louint
em muitos contextos do Framework (não é compatível com o tipo).Geralmente, você deve sempre usar o tipo de dados mais específico possível para os seus dados.
Se, por exemplo, você estiver usando o Entity Framework para extrair dados de um banco de dados, o EF usará automaticamente o tipo de dados mais próximo do usado no banco de dados.
Há dois problemas com isso em c #.
Primeiro, a maioria dos desenvolvedores de C # usa apenas
int
para representar números inteiros (a menos que haja um motivo para usá-lolong
). Isso significa que outros desenvolvedores não pensarão em verificar o tipo de dados e, portanto, receberão os erros de estouro mencionados acima. A segunda, e assunto mais crítico, é / era que do .NET operadores aritméticos originais suportado apenasint
,uint
,long
,ulong
,float
, duplos, edecimal
*. Esse ainda é o caso hoje (consulte a seção 7.8.4 na especificação de idioma do C # 5.0 ). Você pode testar isso usando o seguinte código:O resultado do nosso
byte
-byte
é umint
(System.Int32
).Esses dois problemas deram origem à prática do "único uso int para números inteiros", que é tão comum.
Portanto, para responder sua pergunta, em C # geralmente é uma boa ideia manter-se, a
int
menos que:byte
e umint
ou umint
e a e along
é crítica, ou as diferenças aritméticas dos não assinados já mencionados).Se você precisar fazer cálculos nos dados, siga os tipos comuns.
Lembre-se, você pode transmitir de um tipo para outro. Isso pode ser menos eficiente do ponto de vista da CPU, então você provavelmente está melhor com um dos sete tipos comuns, mas é uma opção, se necessário.
Enumerações (
enum
) é uma das minhas exceções pessoais às diretrizes acima. Se eu tiver apenas algumas opções, especificarei a enumeração como um byte ou um curto. Se eu precisar desse último bit em uma enum sinalizada, especificarei o tipo a seruint
usado para que eu possa usar hex para definir o valor da sinalização.Se você usar uma propriedade com código de restrição de valor, não deixe de explicar na tag de resumo quais restrições existem e por quê.
* Aliases de C # são usados em vez de nomes de .NET, como se
System.Int32
trata de uma pergunta de C #.Nota: havia um blog ou artigo dos desenvolvedores .NET (que não consigo encontrar), que apontava o número limitado de funções aritméticas e algumas razões pelas quais eles não se preocupavam. Pelo que me lembro, eles indicaram que não tinham planos de adicionar suporte para os outros tipos de dados.
Nota: Java não suporta tipos de dados não assinados e anteriormente não tinha suporte para números inteiros de 8 ou 16 bits. Como muitos desenvolvedores de C # vieram de Java ou precisavam trabalhar em ambas as linguagens, as limitações de uma linguagem às vezes seriam artificialmente impostas à outra.
fonte
Você precisa estar ciente principalmente de duas coisas: os dados que está representando e quaisquer etapas intermediárias em seus cálculos.
Certamente faz sentido ter idade
unsigned int
, porque geralmente não consideramos idades negativas. Mas então você menciona subtrair uma idade de outra. Se apenas subtrairmos cegamente um número inteiro de outro, é definitivamente possível acabar com um número negativo, mesmo que tenhamos concordado anteriormente que idades negativas não fazem sentido. Portanto, nesse caso, você deseja que seu cálculo seja feito com um número inteiro assinado.Quanto a saber se os valores não assinados são ruins ou não, eu diria que é uma generalização enorme dizer que os valores não assinados são ruins. Java não possui valores não assinados, como você mencionou, e isso constantemente me incomoda. A
byte
pode ter um valor de 0-255 ou 0x00-0xFF. Mas se você deseja instanciar um byte maior que 127 (0x7F), é necessário escrevê-lo como um número negativo ou converter um número inteiro em um byte. Você acaba com um código parecido com este:O exposto acima me irrita sem fim. Não tenho permissão para que um byte tenha um valor de 197, mesmo que seja um valor perfeitamente válido para a maioria das pessoas sãs que lidam com bytes. Posso converter o número inteiro ou encontrar o valor negativo (197 == -59 neste caso). Considere também isso:
Como você pode ver, adicionar dois bytes com valores válidos e terminar com um byte com um valor válido acaba alterando o sinal. Não apenas isso, mas não é imediatamente óbvio que 70 + 80 == -106. Tecnicamente, isso é um estouro, mas, na minha opinião (como ser humano), um byte não deve estourar para valores abaixo de 0xFF. Quando faço bit aritmético no papel, não considero o oitavo bit um sinal.
Eu trabalho com muitos números inteiros no nível de bits, e ter tudo assinado normalmente torna tudo menos intuitivo e mais difícil de lidar, porque você deve se lembrar que mudar um número negativo à direita dá a você novos
1
s no seu número. Enquanto o deslocamento à direita de um número inteiro sem sinal nunca faz isso. Por exemplo:Apenas adiciona etapas extras que considero não necessárias.
Enquanto eu usei
byte
acima, o mesmo se aplica aos números inteiros de 32 e 64 bits. Não terunsigned
é incapacitante e me choca o fato de existirem linguagens de alto nível, como Java, que não as permitem. Mas para a maioria das pessoas isso não é problema, porque muitos programadores não lidam com aritmética em nível de bit.No final, é útil usar números inteiros não assinados se você estiver pensando neles como bits, e é útil usar números inteiros assinados quando estiver pensando neles como números.
fonte