Ler em inglês

Compartilhar via


IntPtr numérico

Observação

Este artigo é uma especificação de recurso. A especificação serve como o documento de design para o recurso. Ela inclui alterações de especificação propostas, juntamente com as informações necessárias durante o design e o desenvolvimento do recurso. Esses artigos são publicados até que as alterações de especificação propostas sejam finalizadas e incorporadas na especificação ECMA atual.

Pode haver algumas divergências entre a especificação do recurso e a implementação concluída. Essas diferenças são capturadas nas notas pertinentes da reunião de design de idioma (LDM).

Você pode saber mais sobre o processo de adoção de speclets de recursos no padrão de linguagem C# no artigo sobre as especificações de .

Problema do especialista: https://github.com/dotnet/csharplang/issues/6065

Resumo

Essa é uma revisão do recurso inicial de inteiros nativos (especificação), onde os tipos nint/nuint eram distintos dos tipos subjacentes System.IntPtr/System.UIntPtr. Em suma, agora tratamos nint/nuint como alias simples dos tipos System.IntPtr/System.UIntPtr, assim como fazemos para int em relação a System.Int32. O sinalizador de recurso em tempo de execução System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr dispara esse novo comportamento.

Design

8.3.5 Tipos simples

O C# fornece um conjunto de tipos de struct predefinidos chamados de tipos simples. Os tipos simples são identificados por meio de palavras-chave, mas essas palavras-chave são meramente aliases para tipos struct predefinidos no namespace System, conforme descrito na tabela a seguir.

Palavra-chave Tipo com alias
sbyte System.SByte
byte System.Byte
short System.Int16
ushort System.UInt16
int System.Int32
uint System.UInt32
nint System.IntPtr
nuint System.UIntPtr
long System.Int64
ulong System.UInt64
char System.Char
float System.Single
double System.Double
bool System.Boolean
decimal System.Decimal

[...]

8.3.6 Tipos integrais

O C# dá suporte a onze tipos integrais: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulonge char. [...]

8.8 Tipos não gerenciados

Em outras palavras, um unmanaged_type é um dos seguintes:

  • sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal, ou bool.
  • Any enum_type.
  • Qualquer struct_type definido pelo usuário que não seja tipo construído e contenha somente campos de tipos não gerenciados.
  • Em código não seguro, qualquer pointer_type.

10.2.3 Conversões numéricas implícitas

As conversões numéricas implícitas são:

  • De sbyte para short, int, nint, long, float, double ou decimal.
  • De byte para short, ushort, int, uint, nint, nuint, long, ulong, float, double ou decimal.
  • De short para int, nint, long, float, double ou decimal.
  • De ushort para int, uint, nint, nuint, long, ulong, float, double ou decimal.
  • De int para nint, long, float, double ou decimal.
  • De uint para nuint, long, ulong, float, double ou decimal.
  • De nint para long, float, double ou decimal.
  • De nuint para ulong, float, double ou decimal.
  • De long para float, double ou decimal.
  • De ulong para float, double ou decimal.
  • De char para ushort, int, uint, nint, nuint, long, ulong, float, double ou decimal.
  • De float para double.

[...]

10.2.11 Conversões de expressão de constante implícita

Uma conversão de expressão constante implícita permite as conversões a seguir:

  • Uma constant_expression do tipo int pode ser convertida no tipo type sbyte, byte, short, ushort, uint, nint, nuint ou ulong, desde que o valor da constant_expression esteja dentro do intervalo do tipo de destino. [...]

10.3.2 Conversões numéricas explícitas

As conversões numéricas explícitas são as conversões de uma numeric_type para outra numeric_type para a qual ainda não existe uma conversão numérica implícita:

  • De sbyte para byte, ushort, uint, nuint, ulong ou char.
  • De byte para sbyte ou char.
  • De short para sbyte, byte, ushort, uint, nuint, ulong ou char.
  • De ushort para sbyte, byte, short ou char.
  • De int para sbyte, byte, short, ushort, uint, nuint, ulong ou char.
  • De uint para sbyte, byte, short, ushort, int, nint ou char.
  • De long para sbyte, byte, short, ushort, int, uint, nint, nuint, ulong ou char.
  • De nint para sbyte, byte, short, ushort, int, uint, nuint, ulong ou char.
  • De nuint para sbyte, byte, short, ushort, int, uint, nint, long ou char.
  • De ulong para sbyte, byte, short, ushort, int, uint, nint, nuint, long ou char.
  • De char para sbyte, byte ou short.
  • De float para sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char ou decimal.
  • De double para sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float ou decimal.
  • De decimal para sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float ou double.

[...]

10.3.3 Conversões de enumeração explícitas

As conversões de enumeração explícitas são:

  • De sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double ou decimal a qualquer enum_type.
  • De qualquer enum_type a sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double ou decimal.
  • De qualquer enum_type para qualquer outro enum_type.

12.6.4.7 Melhor destino de conversão

Considerando dois tipos T₁ e T₂, T₁ é um destino de conversão melhor do que T₂ se uma das seguintes condições for atendida:

  • Existe uma conversão implícita de T₁ para T₂ e não existe nenhuma conversão implícita de T₂ para T₁
  • T₁ é Task<S₁>, T₂ é Task<S₂> e S₁ é um destino de conversão melhor do que S₂
  • T₁ é S₁ ou S₁? em que S₁ é um tipo integral assinado e T₂ é S₂ ou S₂? em que S₂ é um tipo integral sem sinal. Especificamente: [...]

12.8.12 Acesso ao elemento

[...] O número de expressões no argument_list deve ser igual à classificação de array_type, e cada expressão deve ser do tipo int, uint, nint, nuint, long, ou ulong, ou deve ser implicitamente conversível para um ou mais desses tipos.

11.8.12.2 Acesso de matriz

[...] O número de expressões no argument_list deve ser igual à classificação de array_type, e cada expressão deve ser do tipo int, uint, nint, nuint, long, ou ulong, ou deve ser implicitamente conversível para um ou mais desses tipos.

[...] O processamento em tempo de execução de um acesso de matriz do formulário P[A], em que P é um primary_no_array_creation_expression de um array_type e A é um argument_list, consiste nas seguintes etapas: [...]

  • As expressões de índice de argument_list são avaliadas em ordem, da esquerda para a direita. Após a avaliação de cada expressão de índice, uma conversão implícita para um dos seguintes tipos é executada: int, uint, nint, nuint, long, ulong. O primeiro tipo nessa lista para o qual existe uma conversão implícita é escolhido. [...]

12.8.16 Operadores de incremento e de decremento pós-fixados

A resolução de sobrecarga de operador unário é aplicada para selecionar uma implementação de operador específica. Os operadores predefinidos ++ e -- existem para os seguintes tipos: sbyte, byte, short, ushort, int, uint, nint, nuint,long, ulong, char, float, double, decimal e qualquer tipo de enumeração.

12.9.2 Operador de adição de unário

Os operadores de adição de unários predefinidos são:

C#
...
nint operator +(nint x);
nuint operator +(nuint x);

12.9.3 Operador de subtração de unário

Os operadores de subtração de unários predefinidos são:

  • Negação de inteiro:

    C#
    ...
    nint operator –(nint x);
    

12.8.16 Operadores de incremento e de decremento pós-fixados

Os operadores predefinidos ++ e -- existem para os seguintes tipos: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal e qualquer tipo de enumeração.

11.7.19 Expressões de valor padrão

Além disso, um default_value_expression será uma expressão constante se o tipo for um dos seguintes tipos de valor: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal, bool, ou qualquer tipo de enumeração.

12.9.5 Operador de complemento bit a bit

Os operadores de complemento bit a bit predefinidos são:

C#
...
nint operator ~(nint x);
nuint operator ~(nuint x);

12.9.6 Operadores de incremento e de decremento pré-fixados

Os operadores predefinidos ++ e -- existem para os seguintes tipos: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal e qualquer tipo de enumeração.

12.10 Operadores aritméticos

12.10.2 Operador de multiplicação

Os operadores de multiplicação predefinidos estão listados abaixo. Todos os operadores computam o produto de x e y.

  • Multiplicação de inteiros

    C#
    ...
    nint operator *(nint x, nint y);
    nuint operator *(nuint x, nuint y);
    

12.10.3 Operador de divisão

Os operadores de divisão predefinidos estão listados abaixo. Todos os operadores computam o quociente de x e y.

  • Divisão de inteiros:

    C#
    ...
    nint operator /(nint x, nint y);
    nuint operator /(nuint x, nuint y);
    

12.10.4 Operador de resto

Os operadores restantes predefinidos estão listados abaixo. Todos os operadores computam o restante da divisão entre x e y.

  • Resto inteiro:

    C#
    ...
    nint operator %(nint x, nint y);
    nuint operator %(nuint x, nuint y);
    

12.10.5 Operador de adição

  • Adição de inteiros:

    C#
    ...
    nint operator +(nint x, nint y);
    nuint operator +(nuint x, nuint y);
    

12.10.6 Operador de subtração

  • Subtração de inteiros

    C#
    ...
    nint operator –(nint x, nint y);
    nuint operator –(nuint x, nuint y);
    

12.11 Operadores de deslocamento

Os operadores de deslocamento predefinidos estão listados abaixo.

  • Deslocar à esquerda:

    C#
    ...
    nint operator <<(nint x, int count);
    nuint operator <<(nuint x, int count);
    
  • Deslocar à direita:

    C#
    ...
    nint operator >>(nint x, int count);
    nuint operator >>(nuint x, int count);
    

    O operador >> desloca x para a direita por um número de bits calculado conforme descrito abaixo.

    Quando x é do tipo int, nint long, os bits de ordem baixa de x são descartados, os bits restantes são deslocados para a direita e as posições de bits vazias de ordem alta são definidas como zero se x não for negativo e definido como um se x for negativo.

    Quando x é do tipo uint, nuint ou ulong os bits de ordem baixa de x são descartados, os bits restantes são deslocados para a direita e as posições de bits vazias de ordem alta são definidas como zero.

  • Deslocamento para a direita sem sinal:

    C#
    ...
    nint operator >>>(nint x, int count);
    nuint operator >>>(nuint x, int count);
    

Para os operadores predefinidos, o número de bits a serem deslocados é computado da seguinte maneira: [...]

  • Quando o tipo de x é nint ou nuint, a contagem de deslocamento é fornecida pelos cinco bits de ordem baixa de count em uma plataforma de 32 bits ou pelos seis bits de ordem baixa de count em uma plataforma de 64 bits.

12.12 Operadores de teste de tipo e relacional

12.12.2 Operadores de comparação inteiros

Os operadores de comparação de inteiro predefinidos são:

C#
...
bool operator ==(nint x, nint y);
bool operator ==(nuint x, nuint y);

bool operator !=(nint x, nint y);
bool operator !=(nuint x, nuint y);

bool operator <(nint x, nint y);
bool operator <(nuint x, nuint y);

bool operator >(nint x, nint y);
bool operator >(nuint x, nuint y);

bool operator <=(nint x, nint y);
bool operator <=(nuint x, nuint y);

bool operator >=(nint x, nint y);
bool operator >=(nuint x, nuint y);

12.12 Operadores lógicos

12.12.2 Operadores lógicos inteiros

Os operadores lógicos inteiros predefinidos são:

C#
...
nint operator &(nint x, nint y);
nuint operator &(nuint x, nuint y);

nint operator |(nint x, nint y);
nuint operator |(nuint x, nuint y);

nint operator ^(nint x, nint y);
nuint operator ^(nuint x, nuint y);

12.22 Expressões constantes

Uma expressão constante pode ser um tipo de valor ou um tipo de referência. Se uma expressão constante for um tipo de valor, deverá ser um dos seguintes tipos: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal, bool, ou qualquer tipo de enumeração.

[...]

Uma conversão de expressão constante implícita (§10.2.11) permite que uma expressão constante do tipo int seja convertida em sbyte, byte, short, ushort, uint, nint, nuint, ou ulong, desde que o valor da expressão constante esteja dentro do intervalo do tipo de destino.

17.4 Acesso de elemento de matriz

Os elementos de matriz são acessados usando expressões element_access do formulário A[I₁, I₂, ..., Iₓ], em que A é uma expressão de um tipo de matriz e cada Iₑ é uma expressão do tipo int, uint, nint, nuint,long, ulong ou pode ser convertido implicitamente em um ou mais desses tipos. O resultado de um acesso de elemento de matriz é uma variável, ou seja, o elemento de matriz selecionado pelos índices.

23.5 Conversões de ponteiro

23.5.1 Geral

[...]

Além disso, em um contexto não seguro, o conjunto de conversões explícitas disponíveis é estendido para que inclua as seguintes conversões explícitas de ponteiro:

  • De qualquer pointer_type para qualquer outro pointer_type.
  • De sbyte, byte, short, ushort, int, uint, nint, nuint,longou ulong a qualquer pointer_type.
  • De qualquer pointer_type a sbyte, byte, short, ushort, int, uint, nint, nuint,long ou ulong.

23.6.4 Acesso de elemento do ponteiro

[...] Em um acesso de elemento de ponteiro do formulário P[E], P deve ser uma expressão de um tipo de ponteiro que não seja void*e E deve ser uma expressão que pode ser convertida implicitamente em int, uint, nint, nuint,longou ulong.

23.6.7 Aritmética de Ponteiros

Em um contexto não seguro, o operador + e o operador podem ser aplicados a valores de todos os tipos de ponteiro, exceto void*. Portanto, para cada tipo de ponteiro T*, os seguintes operadores são definidos implicitamente:

C#
[...]
T* operator +(T* x, nint y);
T* operator +(T* x, nuint y);
T* operator +(nint x, T* y);
T* operator +(nuint x, T* y);
T* operator -(T* x, nint y);
T* operator -(T* x, nuint y);

Dada uma expressão P de um tipo de ponteiro T* e uma expressão N do tipo int, uint, nint, nuint, long ou ulong, as expressões P + N e N + P computam o valor do ponteiro do tipo T* que resulta da adição de N * sizeof(T) ao endereço fornecido pelo P. Da mesma forma, a expressão P – N calcula o valor do ponteiro do tipo T* que resulta da subtração de N * sizeof(T) do endereço fornecido por P.

Várias considerações

Alterações da falha

Um dos principais impactos desse design é que System.IntPtr e System.UIntPtr ganham alguns operadores embutidos (conversões, unários e binários).
Eles incluem os operadores checked, o que significa que os seguintes operadores nesses tipos agora lançarão uma exceção ao ocorrer um estouro:

  • IntPtr + int
  • IntPtr - int
  • IntPtr -> int
  • long -> IntPtr
  • void* -> IntPtr

Codificação de metadados

Esse design significa que nint e nuint podem simplesmente ser emitidos como System.IntPtr e System.UIntPtr, sem usar System.Runtime.CompilerServices.NativeIntegerAttribute.
Da mesma forma, ao carregar metadados, NativeIntegerAttribute pode ser ignorado.