Eventos
17 de mar., 21 - 21 de mar., 10
Junte-se à série de encontros para criar soluções de IA escaláveis com base em casos de uso do mundo real com outros desenvolvedores e especialistas.
Registrar agoraNão há mais suporte para esse navegador.
Atualize o Microsoft Edge para aproveitar os recursos, o suporte técnico e as atualizações de segurança mais recentes.
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/435
Suporte de linguagem para tipos de inteiros assinados e não assinados de tamanho nativo.
A motivação é para cenários de interoperabilidade e para bibliotecas de baixo nível.
Os identificadores nint
e nuint
são novas palavras-chave contextuais que representam tipos inteiros nativos com e sem sinal.
Os identificadores são tratados apenas como palavras-chave quando a pesquisa de nome não encontra um resultado viável nesse local do programa.
nint x = 3;
_ = nint.Equals(x, 3);
Os tipos nint
e nuint
são representados pelos tipos subjacentes System.IntPtr
e System.UIntPtr
com o compilador apresentando conversões e operações adicionais para esses tipos como ints nativos.
As expressões constantes podem ser do tipo nint
ou nuint
.
Não há sintaxe direta para inteiros literais nativos. Em vez disso, podem ser usadas conversões implícitas ou explícitas de outros valores integrais constantes: const nint i = (nint)42;
.
Constantesnint
estão no intervalo [ int.MinValue
, int.MaxValue
].
Constantesnuint
estão no intervalo [ uint.MinValue
, uint.MaxValue
].
Não há campos MinValue
ou MaxValue
em nint
ou nuint
porque, além de nuint.MinValue
, esses valores não podem ser emitidos como constantes.
Há suporte para dobragem constante para todos os operadores unários { +
, -
, ~
} e operadores binários { +
, -
, *
, /
, %
, ==
, !=
, <
, <=
, >
, >=
, &
, |
, ^
, <<
, >>
}.
As operações de dobragem constante são avaliadas com os operandos Int32
e UInt32
em vez de ints nativos para comportamento consistente, independentemente da plataforma do compilador.
Se a operação resultar em um valor constante de 32 bits, a dobragem constante é realizado no tempo de compilação.
Caso contrário, a operação será executada em runtime e não será considerada uma constante.
Há uma conversão de identidade entre nint
e IntPtr
e entre nuint
e UIntPtr
.
Há uma conversão de identidade entre tipos compostos que diferem apenas por ints nativos e tipos subjacentes: matrizes, Nullable<>
, tipos construídos e tuplas.
As tabelas abaixo abrangem as conversões entre tipos especiais.
(O IL para cada conversão inclui as variantes para os contextos unchecked
e checked
, se forem diferentes.)
Observações gerais sobre a tabela abaixo:
conv.u
é uma conversão de extensão zero para inteiro nativo e conv.i
é uma conversão com extensão de sinal para um inteiro nativo.checked
para ambos expansão e estreitamento são: conv.ovf.*
para signed to *
conv.ovf.*.un
para unsigned to *
unchecked
para expansão são: conv.i*
para signed to *
(em que * é a largura de destino)conv.u*
para unsigned to *
(em que * é a largura de destino)unchecked
para estreitamento são: conv.i*
para any to signed *
(em que * é a largura de destino)conv.u*
para any to unsigned *
(em que * é a largura de destino)Seguindo alguns exemplos:
sbyte to nint
e sbyte to nuint
usam conv.i
enquanto byte to nint
e byte to nuint
usam conv.u
porque todos eles são de expansão.nint to byte
e nuint to byte
usam conv.u1
enquanto nint to sbyte
e nuint to sbyte
usam conv.i1
. Para byte
, sbyte
, short
e ushort
o "tipo de pilha" é int32
. Portanto, conv.i1
é efetivamente "downcast para um byte assinado e, em seguida, estender o sinal até int32", enquanto conv.u1
é efetivamente "downcast para um byte sem sinal e, em seguida, extensão zero até int32".checked void* to nint
usa conv.ovf.i.un
da mesma maneira que checked void* to long
usa conv.ovf.i8.un
.Operando | Destino | Conversão | Illinois |
---|---|---|---|
object |
nint |
Conversão unboxing | unbox |
void* |
nint |
PointerToVoid | nop / conv.ovf.i.un |
sbyte |
nint |
ImplicitNumeric | conv.i |
byte |
nint |
ImplicitNumeric | conv.u |
short |
nint |
ImplicitNumeric | conv.i |
ushort |
nint |
ImplicitNumeric | conv.u |
int |
nint |
ImplicitNumeric | conv.i |
uint |
nint |
ExplicitNumeric | conv.u / conv.ovf.i.un |
long |
nint |
ExplicitNumeric | conv.i / conv.ovf.i |
ulong |
nint |
ExplicitNumeric | conv.i / conv.ovf.i.un |
char |
nint |
ImplicitNumeric | conv.u |
float |
nint |
ExplicitNumeric | conv.i / conv.ovf.i |
double |
nint |
ExplicitNumeric | conv.i / conv.ovf.i |
decimal |
nint |
ExplicitNumeric | long decimal.op_Explicit(decimal) conv.i / ... conv.ovf.i |
IntPtr |
nint |
Identidade | |
UIntPtr |
nint |
Nenhum | |
object |
nuint |
Conversão unboxing | unbox |
void* |
nuint |
PointerToVoid | nop |
sbyte |
nuint |
ExplicitNumeric | conv.i / conv.ovf.u |
byte |
nuint |
ImplicitNumeric | conv.u |
short |
nuint |
ExplicitNumeric | conv.i / conv.ovf.u |
ushort |
nuint |
ImplicitNumeric | conv.u |
int |
nuint |
ExplicitNumeric | conv.i / conv.ovf.u |
uint |
nuint |
ImplicitNumeric | conv.u |
long |
nuint |
ExplicitNumeric | conv.u / conv.ovf.u |
ulong |
nuint |
ExplicitNumeric | conv.u / conv.ovf.u.un |
char |
nuint |
ImplicitNumeric | conv.u |
float |
nuint |
ExplicitNumeric | conv.u / conv.ovf.u |
double |
nuint |
ExplicitNumeric | conv.u / conv.ovf.u |
decimal |
nuint |
ExplicitNumeric | ulong decimal.op_Explicit(decimal) conv.u / ... conv.ovf.u.un |
IntPtr |
nuint |
Nenhum | |
UIntPtr |
nuint |
Identidade | |
Enumeração | nint |
ExplicitEnumeration | |
Enumeração | nuint |
ExplicitEnumeration |
Operando | Destino | Conversão | Illinois |
---|---|---|---|
nint |
object |
Conversão boxing | box |
nint |
void* |
PointerToVoid | nop / conv.ovf.u |
nint |
nuint |
ExplicitNumeric | conv.u (pode ser omitido) / conv.ovf.u |
nint |
sbyte |
ExplicitNumeric | conv.i1 / conv.ovf.i1 |
nint |
byte |
ExplicitNumeric | conv.u1 / conv.ovf.u1 |
nint |
short |
ExplicitNumeric | conv.i2 / conv.ovf.i2 |
nint |
ushort |
ExplicitNumeric | conv.u2 / conv.ovf.u2 |
nint |
int |
ExplicitNumeric | conv.i4 / conv.ovf.i4 |
nint |
uint |
ExplicitNumeric | conv.u4 / conv.ovf.u4 |
nint |
long |
ImplicitNumeric | conv.i8 |
nint |
ulong |
ExplicitNumeric | conv.i8 / conv.ovf.u8 |
nint |
char |
ExplicitNumeric | conv.u2 / conv.ovf.u2 |
nint |
float |
ImplicitNumeric | conv.r4 |
nint |
double |
ImplicitNumeric | conv.r8 |
nint |
decimal |
ImplicitNumeric | conv.i8 decimal decimal.op_Implicit(long) |
nint |
IntPtr |
Identidade | |
nint |
UIntPtr |
Nenhum | |
nint |
Enumeração | ExplicitEnumeration | |
nuint |
object |
Conversão boxing | box |
nuint |
void* |
PointerToVoid | nop |
nuint |
nint |
ExplicitNumeric | conv.i (pode ser omitido) / conv.ovf.i.un |
nuint |
sbyte |
ExplicitNumeric | conv.i1 / conv.ovf.i1.un |
nuint |
byte |
ExplicitNumeric | conv.u1 / conv.ovf.u1.un |
nuint |
short |
ExplicitNumeric | conv.i2 / conv.ovf.i2.un |
nuint |
ushort |
ExplicitNumeric | conv.u2 / conv.ovf.u2.un |
nuint |
int |
ExplicitNumeric | conv.i4 / conv.ovf.i4.un |
nuint |
uint |
ExplicitNumeric | conv.u4 / conv.ovf.u4.un |
nuint |
long |
ExplicitNumeric | conv.u8 / conv.ovf.i8.un |
nuint |
ulong |
ImplicitNumeric | conv.u8 |
nuint |
char |
ExplicitNumeric | conv.u2 / conv.ovf.u2.un |
nuint |
float |
ImplicitNumeric | conv.r.un conv.r4 |
nuint |
double |
ImplicitNumeric | conv.r.un conv.r8 |
nuint |
decimal |
ImplicitNumeric | conv.u8 decimal decimal.op_Implicit(ulong) |
nuint |
IntPtr |
Nenhum | |
nuint |
UIntPtr |
Identidade | |
nuint |
Enumeração | ExplicitEnumeration |
A conversão de A
em Nullable<B>
é:
A
para B
;A
para B
;A conversão de Nullable<A>
em B
é:
A
em B
;A conversão de Nullable<A>
em Nullable<B>
é:
A
em B
;A
em B
;Os operadores predefinidos são os seguintes.
Esses operadores são considerados durante a resolução de sobrecarga com base em regras normais para conversões implícitas se pelo menos um dos operandos for do tipo nint
ou nuint
.
(O IL para cada operador inclui as variantes para os contextos unchecked
e checked
, se forem diferentes.)
Unário | Assinatura do Operador | Illinois |
---|---|---|
+ |
nint operator +(nint value) |
nop |
+ |
nuint operator +(nuint value) |
nop |
- |
nint operator -(nint value) |
neg |
~ |
nint operator ~(nint value) |
not |
~ |
nuint operator ~(nuint value) |
not |
Binário | Assinatura do Operador | Illinois |
---|---|---|
+ |
nint operator +(nint left, nint right) |
add / add.ovf |
+ |
nuint operator +(nuint left, nuint right) |
add / add.ovf.un |
- |
nint operator -(nint left, nint right) |
sub / sub.ovf |
- |
nuint operator -(nuint left, nuint right) |
sub / sub.ovf.un |
* |
nint operator *(nint left, nint right) |
mul / mul.ovf |
* |
nuint operator *(nuint left, nuint right) |
mul / mul.ovf.un |
/ |
nint operator /(nint left, nint right) |
div |
/ |
nuint operator /(nuint left, nuint right) |
div.un |
% |
nint operator %(nint left, nint right) |
rem |
% |
nuint operator %(nuint left, nuint right) |
rem.un |
== |
bool operator ==(nint left, nint right) |
beq / ceq |
== |
bool operator ==(nuint left, nuint right) |
beq / ceq |
!= |
bool operator !=(nint left, nint right) |
bne |
!= |
bool operator !=(nuint left, nuint right) |
bne |
< |
bool operator <(nint left, nint right) |
blt / clt |
< |
bool operator <(nuint left, nuint right) |
blt.un / clt.un |
<= |
bool operator <=(nint left, nint right) |
ble |
<= |
bool operator <=(nuint left, nuint right) |
ble.un |
> |
bool operator >(nint left, nint right) |
bgt / cgt |
> |
bool operator >(nuint left, nuint right) |
bgt.un / cgt.un |
>= |
bool operator >=(nint left, nint right) |
bge |
>= |
bool operator >=(nuint left, nuint right) |
bge.un |
& |
nint operator &(nint left, nint right) |
and |
& |
nuint operator &(nuint left, nuint right) |
and |
| |
nint operator |(nint left, nint right) |
or |
| |
nuint operator |(nuint left, nuint right) |
or |
^ |
nint operator ^(nint left, nint right) |
xor |
^ |
nuint operator ^(nuint left, nuint right) |
xor |
<< |
nint operator <<(nint left, int right) |
shl |
<< |
nuint operator <<(nuint left, int right) |
shl |
>> |
nint operator >>(nint left, int right) |
shr |
>> |
nuint operator >>(nuint left, int right) |
shr.un |
Para alguns operadores binários, os operadores IL dão suporte a tipos de operando adicionais (consulte tabela de tipos de operandos ECMA-335 III.1.5). No entanto, o conjunto de tipos de operando com suporte pelo C# é limitado para simplicidade e consistência com operadores existentes na linguagem.
Há suporte para versões levantadas dos operadores, em que os argumentos e os tipos de retorno são nint?
e nuint?
.
As operações de atribuição composta x op= y
em que x
ou y
são ints nativos seguem as mesmas regras que com outros tipos primitivos com operadores predefinidos.
Especificamente, a expressão é associada como x = (T)(x op y)
em que T
é o tipo de x
e onde x
só é avaliado uma vez.
Os operadores de deslocamento devem mascarar o número de bits a serem deslocados, para 5 bits se sizeof(nint)
for 4 e para 6 bits se sizeof(nint)
for 8.
(consulte §12.11) na especificação C#).
O compilador C#9 relatará erros de associação a operadores inteiros nativos predefinidos ao compilar com uma versão de linguagem anterior, mas permitirá o uso de conversões predefinidas de e para inteiros nativos.
csc -langversion:9 -t:library A.cs
public class A
{
public static nint F;
}
csc -langversion:8 -r:A.dll B.cs
class B : A
{
static void Main()
{
F = F + 1; // error: nint operator+ not available with -langversion:8
F = (System.IntPtr)F + 1; // ok
}
}
Não há operadores predefinidos em C# para adição ou subtração de ponteiro com compensações de inteiros nativos.
Em vez disso, os valores nint
e nuint
são promovidos a long
e ulong
, e a aritmética de ponteiros usa operadores predefinidos para esses tipos.
static T* AddLeftS(nint x, T* y) => x + y; // T* operator +(long left, T* right)
static T* AddLeftU(nuint x, T* y) => x + y; // T* operator +(ulong left, T* right)
static T* AddRightS(T* x, nint y) => x + y; // T* operator +(T* left, long right)
static T* AddRightU(T* x, nuint y) => x + y; // T* operator +(T* left, ulong right)
static T* SubRightS(T* x, nint y) => x - y; // T* operator -(T* left, long right)
static T* SubRightU(T* x, nuint y) => x - y; // T* operator -(T* left, ulong right)
O texto informativo das promoções numéricas binárias (consulte §12.4.7.3) na especificação C#) é atualizado da seguinte forma:
- …
- Caso contrário, se o operando for do tipo
ulong
, o outro operando será convertido para o tipoulong
ou ocorrerá um erro de tempo de associação se o outro operando for do tiposbyte
,short
,int
,nint
oulong
.- Caso contrário, se o operando for do tipo
nuint
, o outro operando será convertido para o tiponuint
ou ocorrerá um erro de tempo de associação se o outro operando for do tiposbyte
,short
,int
,nint
oulong
.- Caso contrário, se qualquer operando for do tipo
long
, o outro operando será convertido para o tipolong
.- Caso contrário, se o operando for do tipo
uint
e o outro operando for do tiposbyte
,short
,nint
, ouint
, os dois operandos serão convertidos para o tipolong
.- Caso contrário, se qualquer operando for do tipo
uint
, o outro operando será convertido para o tipouint
.- Caso contrário, se qualquer operando for do tipo
nint
, o outro operando será convertido para o tiponint
.- Caso contrário, os dois operandos serão convertidos no tipo
int
.
As conversões e os operadores são sintetizados pelo compilador e não fazem parte dos tipos de IntPtr
e UIntPtr
subjacentes.
Como resultado, essas conversões e operadores não estão disponíveis a partir do associador de runtime para dynamic
.
nint x = 2;
nint y = x + x; // ok
dynamic d = x;
nint z = d + x; // RuntimeBinderException: '+' cannot be applied 'System.IntPtr' and 'System.IntPtr'
O único construtor para nint
ou nuint
é o construtor sem parâmetro.
Os seguintes membros de System.IntPtr
e System.UIntPtr
são excluídos explicitamente de nint
ou nuint
.
// constructors
// arithmetic operators
// implicit and explicit conversions
public static readonly IntPtr Zero; // use 0 instead
public static int Size { get; } // use sizeof() instead
public static IntPtr Add(IntPtr pointer, int offset);
public static IntPtr Subtract(IntPtr pointer, int offset);
public int ToInt32();
public long ToInt64();
public void* ToPointer();
Os membros restantes de System.IntPtr
e System.UIntPtr
estão implicitamente incluídos em nint
e nuint
. Para .NET Framework 4.7.2:
public override bool Equals(object obj);
public override int GetHashCode();
public override string ToString();
public string ToString(string format);
As interfaces implementadas por System.IntPtr
e System.UIntPtr
são incluídas implicitamente em nint
e nuint
, com ocorrências dos tipos subjacentes substituídos pelos tipos inteiros nativos correspondentes.
Por exemplo, se IntPtr
implementa ISerializable, IEquatable<IntPtr>, IComparable<IntPtr>
, então nint
implementa ISerializable, IEquatable<nint>, IComparable<nint>
.
nint
, System.IntPtr
, nuint
e System.UIntPtr
, são considerados equivalentes para substituir, ocultar e implantar.
As sobrecargas não podem ser diferentes apenas por nint
, System.IntPtr
, nuint
e System.UIntPtr
.
As substituições e implantações podem diferir apenas por nint
e System.IntPtr
ou nuint
e System.UIntPtr
.
Os métodos ocultam outros métodos que diferem apenas por nint
e System.IntPtr
, ou nuint
e System.UIntPtr
.
As expressões nint
e nuint
usadas como índices de matriz são emitidas sem conversão.
static object GetItem(object[] array, nint index)
{
return array[index]; // ok
}
nint
e nuint
não podem ser usados como um tipo base enum
de C#.
enum E : nint // error: byte, sbyte, short, ushort, int, uint, long, or ulong expected
{
}
Leituras e gravações são atômicas para nint
e nuint
.
Os campos podem ser marcados volatile
para os tipos nint
e nuint
.
No entanto, ECMA-334 15.5.4 não inclui enum
com tipo base System.IntPtr
ou System.UIntPtr
.
default(nint)
e new nint()
são equivalentes a (nint)0
; default(nuint)
e new nuint()
são equivalentes a (nuint)0
.
typeof(nint)
é typeof(IntPtr)
; typeof(nuint)
é typeof(UIntPtr)
.
sizeof(nint)
e sizeof(nuint)
têm suporte, mas exigem compilação em um contexto não seguro (conforme necessário para sizeof(IntPtr)
e sizeof(UIntPtr)
).
Os valores não são constantes de tempo de compilação.
sizeof(nint)
é implementado como sizeof(IntPtr)
em vez de IntPtr.Size
; sizeof(nuint)
é implementado como sizeof(UIntPtr)
em vez de UIntPtr.Size
.
Diagnósticos do compilador para referências de tipo que envolvem nint
ou nuint
relatam nint
ou nuint
em vez de IntPtr
ou UIntPtr
.
nint
e nuint
são representados em metadados como System.IntPtr
e System.UIntPtr
.
Referências de tipo que incluem nint
ou nuint
são emitidas com um System.Runtime.CompilerServices.NativeIntegerAttribute
, para indicar quais partes da referência de tipo são ints nativos.
namespace System.Runtime.CompilerServices
{
[AttributeUsage(
AttributeTargets.Class |
AttributeTargets.Event |
AttributeTargets.Field |
AttributeTargets.GenericParameter |
AttributeTargets.Parameter |
AttributeTargets.Property |
AttributeTargets.ReturnValue,
AllowMultiple = false,
Inherited = false)]
public sealed class NativeIntegerAttribute : Attribute
{
public NativeIntegerAttribute()
{
TransformFlags = new[] { true };
}
public NativeIntegerAttribute(bool[] flags)
{
TransformFlags = flags;
}
public readonly bool[] TransformFlags;
}
}
A codificação de referências de tipo com NativeIntegerAttribute
é abordada em NativeIntegerAttribute.md.
Uma alternativa à abordagem de "eliminação de tipo" acima é introduzir novos tipos: System.NativeInt
e System.NativeUInt
.
public readonly struct NativeInt
{
public IntPtr Value;
}
Tipos distintos permitiriam sobrecargas distintas de IntPtr
e permitiriam análises distintas e ToString()
.
Mas haveria mais trabalho para o CLR lidar com esses tipos com eficiência, o que derrota a principal finalidade do recurso - a eficiência.
E a interoperabilidade com o código int nativo existente que usa IntPtr
seria mais difícil.
Outra alternativa é adicionar mais suporte de int nativo para IntPtr
na estrutura, mas sem qualquer suporte específico do compilador.
Quaisquer novas conversões e operações aritméticas serão suportadas pelo compilador automaticamente.
Mas o idioma não forneceria palavras-chave, constantes ou operações de checked
.
Comentários do C# feature specifications
O C# feature specifications é um projeto código aberto. Selecione um link para fornecer comentários:
Eventos
17 de mar., 21 - 21 de mar., 10
Junte-se à série de encontros para criar soluções de IA escaláveis com base em casos de uso do mundo real com outros desenvolvedores e especialistas.
Registrar agora