Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Not
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 finns i de relevanta anteckningarna från språkdesignmötena (LDM).
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/435
Sammanfattning
Språkstöd för inbyggda signerade och osignerade heltalstyper.
Motiveringen är för interop-scenarier och för bibliotek på låg nivå.
Utformning
Identifierarna nint och nuint är nya kontextuella nyckelord som representerar inbyggda signerade och osignerade heltalstyper.
Identifierarna behandlas endast som nyckelord när namnsökningen inte hittar ett genomförbart resultat på den programplatsen.
nint x = 3;
_ = nint.Equals(x, 3);
Typerna nint och nuint representeras av de underliggande typerna System.IntPtr och System.UIntPtr, där kompilatorn tillhandahåller ytterligare konverteringar och operationer för dessa typer som inbyggda heltal.
Konstanter
Konstanta uttryck kan vara av typen nint eller nuint.
Det finns ingen direkt syntax för interna int-literaler. Implicita eller explicita avgjutningar av andra helta konstanta värden kan användas i stället: const nint i = (nint)42;.
              nint konstanter finns i intervallet [ int.MinValue, int.MaxValue ].
              nuint konstanter finns i intervallet [ uint.MinValue, uint.MaxValue ].
Det finns inga MinValue- eller MaxValue fält på nint eller nuint eftersom dessa värden, förutom nuint.MinValue, inte kan genereras som konstanter.
Konstant vikning stöds för alla unary operatorer { +, -, ~ } och binära operatorer { +, -, *, /, %, ==, !=, <, <=, >, >=, &, |, ^, <<, >> }.
Ständiga vikningsåtgärder utvärderas med Int32 och UInt32 operander i stället för inbyggda ints för konsekvent beteende oavsett kompilatorplattform.
Om åtgärden resulterar i ett konstant värde i 32 bitar utförs konstant vikning vid kompileringstid.
Annars utförs operationen vid körning och betraktas inte som en konstant.
Omvandlingar
Det finns en identitetskonvertering mellan nint och IntPtroch mellan nuint och UIntPtr.
Det finns en identitetskonvertering mellan sammansatta typer som enbart skiljer sig åt i fråga om de inbyggda heltalen och de underliggande typerna: arrayer, Nullable<>, konstruerade typer och tupler.
Tabellerna nedan beskriver konverteringarna mellan specialtyper.
(IL för varje konvertering innehåller varianterna för unchecked och checked kontexter om de är olika.)
Allmänna anteckningar i tabellen nedan:
- 
              conv.uär en nollutvidgande konvertering till inbyggt heltal ochconv.iär en teckenutvidgande konvertering till inbyggt heltal.
- 
              checkedkontexter för både utvidgning och inskränkning är:- 
              conv.ovf.*försigned to *
- 
              conv.ovf.*.unförunsigned to *
 
- 
              
- 
              uncheckedkontexter för breddning är:- 
              conv.i*försigned to *(där * är målbredden)
- 
              conv.u*förunsigned to *(där * är målbredden)
 
- 
              
- 
              uncheckedkontexter för avsmalning är:- 
              conv.i*förany to signed *(där * är målbredden)
- 
              conv.u*förany to unsigned *(där * är målbredden)
 
- 
              
Ta några exempel:
- 
              sbyte to nintochsbyte to nuintanvänderconv.imedanbyte to nintochbyte to nuintanvänderconv.ueftersom de alla breddar.
- 
              nint to byteochnuint to byteanvänderconv.u1medannint to sbyteochnuint to sbyteanvänderconv.i1. Förbyte,sbyte,shortochushortär "stacktypen"int32. Såconv.i1är i själva verket "nedkonverterad till en signerad byte och sedan teckenförlängd upp till int32" medanconv.u1i praktiken "nedkonverterad till en osignerad byte och sedan nollförlängd upp till int32".
- 
              checked void* to nintanvänderconv.ovf.i.unpå samma sätt somchecked void* to longanvänderconv.ovf.i8.un.
| Operand | Mål | Omvandling | IL | 
|---|---|---|---|
| object | nint | Öppna förpackningen | unbox | 
| void* | nint | PointerToVoid | nop/ conv.ovf.i.un | 
| sbyte | nint | Implicit Numerisk | conv.i | 
| byte | nint | Implicit Numerisk | conv.u | 
| short | nint | Implicit Numerisk | conv.i | 
| ushort | nint | Implicit Numerisk | conv.u | 
| int | nint | Implicit Numerisk | conv.i | 
| uint | nint | ExplicitNumeriskt | conv.u/conv.ovf.i.un | 
| long | nint | ExplicitNumeriskt | conv.i/conv.ovf.i | 
| ulong | nint | ExplicitNumeriskt | conv.i/conv.ovf.i.un | 
| char | nint | Implicit Numerisk | conv.u | 
| float | nint | ExplicitNumeriskt | conv.i/conv.ovf.i | 
| double | nint | ExplicitNumeriskt | conv.i/conv.ovf.i | 
| decimal | nint | ExplicitNumeriskt | long decimal.op_Explicit(decimal) conv.i/... conv.ovf.i | 
| IntPtr | nint | Identitet | |
| UIntPtr | nint | Ingen | |
| object | nuint | Öppna förpackningen | unbox | 
| void* | nuint | PointerToVoid | Nop | 
| sbyte | nuint | ExplicitNumeriskt | conv.i/conv.ovf.u | 
| byte | nuint | Implicit Numerisk | conv.u | 
| short | nuint | ExplicitNumeriskt | conv.i/conv.ovf.u | 
| ushort | nuint | Implicit Numerisk | conv.u | 
| int | nuint | ExplicitNumeriskt | conv.i/conv.ovf.u | 
| uint | nuint | Implicit Numerisk | conv.u | 
| long | nuint | ExplicitNumeriskt | conv.u/conv.ovf.u | 
| ulong | nuint | ExplicitNumeriskt | conv.u/conv.ovf.u.un | 
| char | nuint | Implicit Numerisk | conv.u | 
| float | nuint | ExplicitNumeriskt | conv.u/conv.ovf.u | 
| double | nuint | ExplicitNumeriskt | conv.u/conv.ovf.u | 
| decimal | nuint | ExplicitNumeriskt | ulong decimal.op_Explicit(decimal) conv.u/... conv.ovf.u.un | 
| IntPtr | nuint | Ingen | |
| UIntPtr | nuint | Identitet | |
| Uppräkning | nint | ExplicitEnumeration | |
| Uppräkning | nuint | ExplicitEnumeration | 
| Operand | Mål | Omvandling | IL | 
|---|---|---|---|
| nint | object | Boxning | box | 
| nint | void* | PointerToVoid | nop/ conv.ovf.u | 
| nint | nuint | ExplicitNumeriskt | conv.u(kan utelämnas) /conv.ovf.u | 
| nint | sbyte | ExplicitNumeriskt | conv.i1/conv.ovf.i1 | 
| nint | byte | ExplicitNumeriskt | conv.u1/conv.ovf.u1 | 
| nint | short | ExplicitNumeriskt | conv.i2/conv.ovf.i2 | 
| nint | ushort | ExplicitNumeriskt | conv.u2/conv.ovf.u2 | 
| nint | int | ExplicitNumeriskt | conv.i4/conv.ovf.i4 | 
| nint | uint | ExplicitNumeriskt | conv.u4/conv.ovf.u4 | 
| nint | long | Implicit Numerisk | conv.i8 | 
| nint | ulong | ExplicitNumeriskt | conv.i8/conv.ovf.u8 | 
| nint | char | ExplicitNumeriskt | conv.u2/conv.ovf.u2 | 
| nint | float | Implicit Numerisk | conv.r4 | 
| nint | double | Implicit Numerisk | conv.r8 | 
| nint | decimal | Implicit Numerisk | conv.i8 decimal decimal.op_Implicit(long) | 
| nint | IntPtr | Identitet | |
| nint | UIntPtr | Ingen | |
| nint | Uppräkning | ExplicitEnumeration | |
| nuint | object | Boxning | box | 
| nuint | void* | PointerToVoid | Nop | 
| nuint | nint | ExplicitNumeriskt | conv.i(kan utelämnas) /conv.ovf.i.un | 
| nuint | sbyte | ExplicitNumeriskt | conv.i1/conv.ovf.i1.un | 
| nuint | byte | ExplicitNumeriskt | conv.u1/conv.ovf.u1.un | 
| nuint | short | ExplicitNumeriskt | conv.i2/conv.ovf.i2.un | 
| nuint | ushort | ExplicitNumeriskt | conv.u2/conv.ovf.u2.un | 
| nuint | int | ExplicitNumeriskt | conv.i4/conv.ovf.i4.un | 
| nuint | uint | ExplicitNumeriskt | conv.u4/conv.ovf.u4.un | 
| nuint | long | ExplicitNumeriskt | conv.u8/conv.ovf.i8.un | 
| nuint | ulong | Implicit Numerisk | conv.u8 | 
| nuint | char | ExplicitNumeriskt | conv.u2/conv.ovf.u2.un | 
| nuint | float | Implicit Numerisk | conv.r.un conv.r4 | 
| nuint | double | Implicit Numerisk | conv.r.un conv.r8 | 
| nuint | decimal | Implicit Numerisk | conv.u8 decimal decimal.op_Implicit(ulong) | 
| nuint | IntPtr | Ingen | |
| nuint | UIntPtr | Identitet | |
| nuint | Uppräkning | ExplicitEnumeration | 
Konvertering från A till Nullable<B> är:
- en implicit nullbar konvertering om det finns en identitetskonvertering eller implicit konvertering från AtillB;
- en explicit nullbar konvertering om det finns en explicit konvertering från AtillB;
- annars ogiltig.
Konvertering från Nullable<A> till B är:
- en explicit nullbar konvertering om det finns en identitetskonvertering eller implicit eller explicit numerisk konvertering från AtillB;
- annars ogiltig.
Konvertering från Nullable<A> till Nullable<B> är:
- en identitetskonvertering om det finns en identitetskonvertering från AtillB;
- en explicit nullbar konvertering om det finns en implicit eller explicit numerisk konvertering från AtillB;
- annars ogiltig.
Operatörer
De fördefinierade operatorerna är följande.
Dessa operatorer beaktas vid överbelastningsmatchning baserat på normala regler för implicita konverteringar om minst en av operanderna är av typen nint eller nuint.
(IL för varje operator innehåller varianterna för unchecked och checked kontexter om de är olika.)
| Unär | Operator-signatur | IL | 
|---|---|---|
| + | 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är | Operator-signatur | IL | 
|---|---|---|
| + | 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 | 
För vissa binära operatorer stöder IL-operatorerna ytterligare operandtyper (se ECMA-335 III.1.5 Operand-typtabell). Men uppsättningen operandtyper som stöds av C# är begränsad för enkelhetens skull och för konsekvens med befintliga operatorer på språket.
Lyftade versioner av operatorerna, där argumenten och returtyperna är nint? och nuint?, stöds.
Sammansatta tilldelningar x op= y där x eller y är naturliga heltal följer samma regler som med andra primitiva typer och använder fördefinierade operatorer.
Mer specifikt är uttrycket bundet som x = (T)(x op y) där T är typen av x och där x endast utvärderas en gång.
Skiftoperatorerna bör maskera antalet bitar som ska flyttas – till 5 bitar om sizeof(nint) är 4 och till 6 bitar om sizeof(nint) är 8.
(se §12.11) i C#-specifikationen).
C#9-kompilatorn rapporterar felbindning till fördefinierade interna heltalsoperatorer vid kompilering med en tidigare språkversion, men tillåter användning av fördefinierade konverteringar till och från inbyggda heltal.
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
    }
}
Pekarearitmetik
Det finns inga fördefinierade operatorer i C# för pekartillägg eller subtraktion med inbyggda heltalsförskjutningar.
I stället höjs nint och nuint värden till long och ulong och pekarens aritmetik använder fördefinierade operatorer för dessa typer.
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)
Binära numeriska promotioner
De binära numeriska kampanjerna informativ text (se §12.4.7.3) i C#-specifikationen) uppdateras på följande sätt:
- …
- Annars, om någon av operanderna är av typen
ulong, konverteras den andra operanden till typenulong, eller så inträffar ett bindningstidsfel om den andra operanden är av typensbyte,short,int,nintellerlong.- Annars, om operand är av typen
nuint, konverteras den andra operanden till typennuint, eller om ett bindningstidsfel inträffar om den andra operanden är av typensbyte,short,int,nintellerlong.- Om någon av operanderna är av typen
longkonverteras annars den andra operanden till typenlong.- Om någon av operanderna är av typen
uintoch den andra operanden är av typensbyte,short,nint, ellerintkonverteras båda operanderna till typenlong.- Om någon av operanderna är av typen
uintkonverteras annars den andra operanden till typenuint.- Om någon av operanderna är av typen
nintkonverteras annars den andra operanden till typennint.- Annars konverteras båda operanderna till typen
int.
Dynamisk
Konverteringarna och operatorerna syntetiseras av kompilatorn och ingår inte i de underliggande IntPtr och UIntPtr typerna.
Därför är dessa konverteringar och operatorer inte tillgängliga från körningsbindningen för 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'
Typmedlemmar
Den enda konstruktorn för nint eller nuint är konstruktorn utan parameter.
Följande medlemmar i System.IntPtr och System.UIntPtrundantas uttryckligen från nint eller 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();
Återstående medlemmar i System.IntPtr och System.UIntPtringår implicit i nint och nuint. För .NET Framework 4.7.2:
public override bool Equals(object obj);
public override int GetHashCode();
public override string ToString();
public string ToString(string format);
Gränssnitt som implementeras av System.IntPtr och System.UIntPtringår implicit i nint och nuint, med förekomster av de underliggande typerna ersatta av motsvarande interna heltalstyper.
Om IntPtr till exempel implementerar ISerializable, IEquatable<IntPtr>, IComparable<IntPtr>implementerar nintISerializable, IEquatable<nint>, IComparable<nint>.
Åsidosätta, dölja och implementera
              nint och System.IntPtr, och nuint och System.UIntPtr, anses vara likvärdiga för att åsidosätta, dölja och implementera.
Överlagringar kan inte skilja sig åt beroende på nint och System.IntPtr, och nuint och System.UIntPtr, ensamma.
Åsidosättningar och implementeringar kan skilja sig åt beroende på enbart nint och System.IntPtr, eller enbart nuint och System.UIntPtr.
Metoder döljer andra metoder som skiljer sig mellan nint och System.IntPtr, eller nuint och System.UIntPtr, ensamma.
Diverse
              nint och nuint uttryck som används som matrisindex genereras utan konvertering.
static object GetItem(object[] array, nint index)
{
    return array[index]; // ok
}
              nint och nuint kan inte användas som en enum bastyp från C#.
enum E : nint // error: byte, sbyte, short, ushort, int, uint, long, or ulong expected
{
}
Läsningar och skrivningar är atomära för nint och nuint.
Fält kan markeras volatile för typer nint och nuint.
              ECMA-334 15.5.4 omfattar dock inte enum med bastyp System.IntPtr eller System.UIntPtr.
              default(nint) och new nint() motsvarar (nint)0. default(nuint) och new nuint() motsvarar (nuint)0.
              typeof(nint) är typeof(IntPtr); typeof(nuint) är typeof(UIntPtr).
              sizeof(nint) och sizeof(nuint) stöds men kräver kompilering i en osäker kontext (som krävs för sizeof(IntPtr) och sizeof(UIntPtr)).
Värdena är inte kompileringstidskonstanter.
              sizeof(nint) implementeras som sizeof(IntPtr) i stället för IntPtr.Size. sizeof(nuint) implementeras som sizeof(UIntPtr) i stället för UIntPtr.Size.
Kompilatordiagnostik för typreferenser som inbegriper nint eller nuint rapporterar nint eller nuint i stället för IntPtr eller UIntPtr.
Metadata
              nint och nuint representeras i metadata som System.IntPtr och System.UIntPtr.
Typreferenser som innehåller nint eller nuint genereras med en System.Runtime.CompilerServices.NativeIntegerAttribute som anger vilka delar av typreferensen som är inbyggda ints.
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;
    }
}
Kodningen av typreferenser med NativeIntegerAttribute beskrivs i NativeIntegerAttribute.md.
Alternativ
Ett alternativ till metoden "typ radering" ovan är att introducera nya typer: System.NativeInt och System.NativeUInt.
public readonly struct NativeInt
{
    public IntPtr Value;
}
Distinkta typer skulle tillåta överlagring som skiljer sig från IntPtr och skulle tillåta distinkt parsning och ToString().
Men det skulle innebära mer arbete för CLR att effektivt hantera dessa typer, vilket motverkar huvudsyftet med funktionen - effektivitet.
Och interoperation med befintlig int-intern kod som använder IntPtr skulle vara svårare.
Ett annat alternativ är att lägga till mer internt int-stöd för IntPtr i ramverket men utan något specifikt kompilatorstöd.
Alla nya konverteringar och aritmetiska åtgärder stöds automatiskt av kompilatorn.
Men språket skulle inte ge nyckelord, konstanter eller checked åtgärder.
Designa möten
- https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-05-26.md
- https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-06-13.md
- https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-07-05.md#native-int-and-intptr-operators
- https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-10-23.md
- https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-03-25.md
C# feature specifications