Dela via


Numerisk IntPtr

Notera

Den här artikeln är en funktionsspecifikation. Specifikationen fungerar som designdokument för funktionen. Den innehåller föreslagna specifikationsändringar, tillsammans med information som behövs under utformningen och utvecklingen av funktionen. Dessa artiklar publiceras tills de föreslagna specifikationsändringarna har slutförts och införlivats i den aktuella ECMA-specifikationen.

Det kan finnas vissa skillnader mellan funktionsspecifikationen och den slutförda implementeringen. Dessa skillnader dokumenteras i de relevanta anteckningarna från LDM (Language Design Meeting).

Du kan läsa mer om processen för att införa funktionsspecifikationer i C#-språkstandarden i artikeln om specifikationerna.

Champion-fråga: https://github.com/dotnet/csharplang/issues/6065

Sammanfattning

Det här är en revision av den ursprungliga funktionen för inbyggda heltal (spec), där de nint/nuint typerna skiljer sig från de underliggande typerna System.IntPtr/System.UIntPtr. Kort och gott behandlar vi nu nint/nuint som enkla typer av alias System.IntPtr/System.UIntPtr, som vi gör för int i förhållande till System.Int32. Funktionsflaggan för körning System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr utlöser det här nya beteendet.

Design

8.3.5 Enkla typer

C# innehåller en uppsättning fördefinierade struct typer som kallas enkla typer. De enkla typerna identifieras via nyckelord, men de här nyckelorden är helt enkelt alias för fördefinierade struct typer i System namnområde enligt beskrivningen i tabellen nedan.

nyckelord aliastyp
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 Integraltyper

C# stöder elva integraltyper: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulongoch char. [...]

8.8 Ohanterade typer

Med andra ord är en unmanaged_type något av följande:

  • sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimaleller bool.
  • Vilken som helst enum_type.
  • Alla användardefinierade struct_type som inte är en konstruerad typ och som innehåller fält av endast unmanaged_types.
  • I osäker kod används valfri pekartyp pointer_type.

10.2.3 Implicita numeriska konverteringar

De implicita numeriska konverteringarna är:

  • Från sbyte till short, int, nint, long, float, doubleeller decimal.
  • Från byte till short, ushort, int, uint, nint, nuint, long, ulong, float, doubleeller decimal.
  • Från short till int, nint, long, float, doubleeller decimal.
  • Från ushort till int, uint, nint, nuint, long, ulong, float, doubleeller decimal.
  • Från int till nint, long, float, doubleeller decimal.
  • Från uint till nuint, long, ulong, float, doubleeller decimal.
  • Från nint till long, float, doubleeller decimal.
  • Från nuint till ulong, float, doubleeller decimal.
  • Från long till float, doubleeller decimal.
  • Från ulong till float, doubleeller decimal.
  • Från char till ushort, int, uint, nint, nuint, long, ulong, float, doubleeller decimal.
  • Från float till double.

[...]

10.2.11 Implicita konverteringar av konstanta uttryck

En implicit konvertering av konstanta uttryck tillåter följande konverteringar:

  • En constant_expression av typen int kan konverteras till typen sbyte, byte, short, ushort, uint, nint, nuinteller ulong, förutsatt att värdet för constant_expression ligger inom måltypens intervall. [...]

10.3.2 Explicita numeriska konverteringar

Explicita numeriska konverteringar är konverteringar från en numeric_type till en annan numeric_type för vilka det inte redan finns någon implicit numerisk konvertering:

  • Från sbyte till byte, ushort, uint, nuint, ulongeller char.
  • Från byte till sbyte eller char.
  • Från short till sbyte, byte, ushort, uint, nuint, ulongeller char.
  • Från ushort till sbyte, byte, shorteller char.
  • Från int till sbyte, byte, short, ushort, uint, nuint, ulongeller char.
  • Från uint till sbyte, byte, short, ushort, int, ninteller char.
  • Från long till sbyte, byte, short, ushort, int, uint, nint, nuint, ulongeller char.
  • Från nint till sbyte, byte, short, ushort, int, uint, nuint, ulongeller char.
  • Från nuint till sbyte, byte, short, ushort, int, uint, nint, longeller char.
  • Från ulong till sbyte, byte, short, ushort, int, uint, nint, nuint, longeller char.
  • Från char till sbyte, byteeller short.
  • Från float till sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, chareller decimal.
  • Från double till sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, floateller decimal.
  • Från decimal till sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, floateller double.

[...]

10.3.3 Explicita uppräkningskonverteringar

De explicita uppräkningskonverteringarna är:

  • Från sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, doubleeller decimal till alla enum_type.
  • Från alla enum_type till sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, doubleeller decimal.
  • Från vilken som helst enum_type till vilken som helst annan enum_type.

12.6.4.7 Bättre konverteringsmål

Med två typer T₁ och T₂är T₁ ett bättre konverteringsmål än T₂ om något av följande gäller:

  • Det finns en implicit konvertering från T₁ till T₂ och det finns ingen implicit konvertering från T₂ till T₁
  • T₁ är Task<S₁>, T₂ är Task<S₂>, och S₁ är ett bättre konverteringsmål än S₂
  • T₁ är S₁ eller S₁? där S₁ är en signerad integrerad typ och T₂ är S₂ eller S₂? där S₂ är en osignerad integrerad typ. Mer specifikt: [...]

12.8.12 Elementåtkomst

[...] Antalet uttryck i argument_list ska vara samma som rangordningen för array_type, och varje uttryck ska vara av typen int, uint, nint, nuint, long, eller ulong, eller ska implicit konverteras till en eller flera av dessa typer.

11.8.12.2 Matrisåtkomst

[...] Antalet uttryck i argument_list ska vara samma som rangordningen för array_type, och varje uttryck ska vara av typen int, uint, nint, nuint, long, eller ulong, eller ska implicit konverteras till en eller flera av dessa typer.

[...] Körningsbearbetningen av en matrisåtkomst för formuläret P[A], där P är en primary_no_array_creation_expression av en array_type och A är en argument_list, består av följande steg: [...]

  • Indexuttrycken för argument_list utvärderas i ordning, från vänster till höger. Efter utvärdering av varje indexuttryck utförs en implicit konvertering till någon av följande typer: int, uint, nint, nuint, long, ulong. Den första typen i den här listan som en implicit konvertering finns för väljs. [...]

12.8.16 Postfix-inkrements- och minskningsoperatorer

Unary operator overload resolution används för att välja en specifik operatorimplementering. Fördefinierade operatorer för ++ och -- finns för följande typer: sbyte, byte, short, ushort, int, uint, nint, nuint,long, ulong, char, float, double, decimaloch alla uppräkningstyper.

12.9.2 Unary plus operator

De fördefinierade unära plusoperatorerna är:

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

12.9.3 Unär minusoperator

De fördefinierade unary minus-operatorerna är:

  • Heltalsnegation

    ...
    nint operator –(nint x);
    

12.8.16 Postfix-inkrements- och minskningsoperatorer

Fördefinierade operatorer för ++ och -- finns för följande typer: sbyte, byte, short, ushort, int, uint, ,nint, nuint,, long, ulong, char, float, double, decimaloch alla enumtyper.

11.7.19 Standardvärdeuttryck

Dessutom är en default_value_expression ett konstant uttryck om typen är någon av följande värdetyper: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal, bool, eller någon uppräkningstyp.

12.9.5 Bitvis komplementoperator

De fördefinierade bitvis kompletterande operatorerna är:

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

12.9.6 Inkrements- och minskningsoperatorer för prefix

Fördefinierade operatorer för ++ och -- finns för följande typer: sbyte, byte, short, ushort, int, uint, ,nint, nuint,, long, ulong, char, float, double, decimaloch alla enumtyper.

12.10 Aritmetiska operatorer

12.10.2 Multiplikationsoperator

De fördefinierade multiplikationsoperatorerna visas nedan. Operatorerna beräknar alla produkten av x och y.

  • Heltalsmultiplikation:

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

12.10.3 Divisionsoperatör

De fördefinierade divisionsoperatorerna visas nedan. Operatorerna beräknar alla kvoten för x och y.

  • Heltalsdivision:

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

12.10.4 Restoperator

De fördefinierade restoperatorerna visas nedan. Operatorerna beräknar resten av divisionen mellan x och y.

  • Heltalsrest:

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

12.10.5 Additionsoperator

  • Heltalstillägg:

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

12.10.6 Subtraktionsoperator

  • Heltalsundertraktion:

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

12.11 Skiftoperatorer

De fördefinierade skiftoperatorerna visas nedan.

  • Skift vänster:

    ...
    nint operator <<(nint x, int count);
    nuint operator <<(nuint x, int count);
    
  • Skift höger:

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

    Operatorn >> skiftar x rätt med ett antal bitar som beräknas enligt beskrivningen nedan.

    När x är av typen int, nint eller long, ignoreras x:s lågordningsbitar, de återstående bitarna flyttas åt höger, och de tomma högordningsbitpositionerna ställs in på noll om x är icke-negativ och på ett om x är negativ.

    När x är av typen uint, nuint eller ulongignoreras de låga bitarna av x, de återstående bitarna flyttas åt höger och de tomma bitpositionerna i hög ordning är inställda på noll.

  • Osignerat skift till höger:

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

För de fördefinierade operatorerna beräknas antalet bitar som ska flyttas enligt följande: [...]

  • När typen av x är nint eller nuint, bestäms skiftantalet av de fem lägsta bitarna av count på en 32-bitarsplattform, eller av de sex lägsta bitarna av count på en 64-bitarsplattform.

12.12 Relations- och typtestningsoperatorer

12.12.2 Jämförelseoperatorer för heltal

De fördefinierade heltalsjämförelseoperatorerna är:

...
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 Logiska operatorer

12.12.2 Heltalslogiska operatorer

De fördefinierade logiska heltalsoperatorerna är:

...
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 Konstanta uttryck

Ett konstant uttryck kan vara antingen en värdetyp eller en referenstyp. Om ett konstant uttryck är en värdetyp måste det vara någon av följande typer: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal, bool, eller någon uppräkningstyp.

[...]

Med en implicit konvertering av konstanta uttryck kan ett konstant uttryck av typen int konverteras till sbyte, byte, short, ushort, uint, nint, nuint, eller ulong, förutsatt att värdet för det konstanta uttrycket ligger inom måltypens intervall.

17.4 Matriselementåtkomst

Matriselement används med hjälp av element_access uttryck i formuläret A[I₁, I₂, ..., Iₓ], där A är ett uttryck av en matristyp och varje Iₑ är ett uttryck av typen int, uint, nint, nuint,long, ulongeller implicit kan konverteras till en eller flera av dessa typer. Resultatet av en matriselementåtkomst är en variabel, nämligen matriselementet som väljs av indexen.

23.5 Pekarkonverteringar

23.5.1 Allmänt

[...]

I en osäker kontext utökas dessutom uppsättningen med tillgängliga explicita konverteringar till att omfatta följande explicita pekarkonverteringar:

  • Från vilken som helst pointer_type till vilket som helst pointer_type.
  • Från sbyte, byte, short, ushort, int, uint, nint, nuint,longeller ulong till alla pointer_type.
  • Från alla pointer_type till sbyte, byte, short, ushort, int, uint, nint, nuint,longeller ulong.

23.6.4 Åtkomst till pekarelement

[...] I ett pekarelement för formuläret P[E]ska P vara ett uttryck av en annan pekartyp än void*och E ska vara ett uttryck som implicit kan konverteras till int, uint, nint, nuint,longeller ulong.

23.6.7 Pekare-aritmetik

I ett osäkert sammanhang kan operatorn + och tillämpas på värden för alla pekartyper förutom void*. För varje pekartyp T*definieras följande operatorer implicit:

[...]
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);

Med ett uttryck P av en pekartyp T* och ett uttryck N av typen int, uint, nint, nuint,longeller ulong, beräknar uttrycken P + N och N + P pekarvärdet för typen T* som resulterar i att N * sizeof(T) läggs till i adressen som anges av P. På samma sätt beräknar uttrycket P – N pekarvärdet av typen T* som är resultatet av att subtrahera N * sizeof(T) från adressen som anges av P.

Olika överväganden

Icke-bakåtkompatibla ändringar

En av de viktigaste effekterna av den här designen är att System.IntPtr och System.UIntPtr får några inbyggda operatorer (konverteringar, unära och binära).
Dessa omfattar checked-operatörer, vilket innebär att följande operatörer på dessa typer nu kommer att utlösas vid överbelastning:

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

Metadatakodning

Den här designen innebär att nint och nuint helt enkelt kan genereras som System.IntPtr och System.UIntPtr, utan att använda System.Runtime.CompilerServices.NativeIntegerAttribute.
På samma sätt kan NativeIntegerAttribute ignoreras när metadata läses in.