Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Notitie
Dit artikel is een functiespecificatie. De specificatie fungeert als het ontwerpdocument voor de functie. Het bevat voorgestelde specificatiewijzigingen, samen met informatie die nodig is tijdens het ontwerp en de ontwikkeling van de functie. Deze artikelen worden gepubliceerd totdat de voorgestelde specificaties zijn voltooid en opgenomen in de huidige ECMA-specificatie.
Er kunnen enkele verschillen zijn tussen de functiespecificatie en de voltooide implementatie. Deze verschillen worden vastgelegd in de relevante LDM-notities (Language Design Meeting) .
Meer informatie over het proces voor het aannemen van functiespeclets in de C#-taalstandaard vindt u in het artikel over de specificaties.
Kampioenprobleem: https://github.com/dotnet/csharplang/issues/435
Samenvatting
Taalondersteuning voor geheel getaltypes met inheemse grootte, zowel gesigneerd als niet-gesigneerd.
De motivatie is voor interop-scenario's en voor bibliotheken op laag niveau.
Ontwerpen
De id's nint en nuint zijn nieuwe contextuele trefwoorden die systeemeigen ondertekende en niet-ondertekende gehele getallen vertegenwoordigen.
De id's worden alleen behandeld als trefwoorden wanneer de naamzoekactie geen levensvatbaar resultaat op die programmalocatie vindt.
nint x = 3;
_ = nint.Equals(x, 3);
De typen nint en nuint worden vertegenwoordigd door de onderliggende typen System.IntPtr en System.UIntPtr, waarbij de compiler extra conversies en bewerkingen voor deze typen als systeemeigen gehele getallen mogelijk maakt.
Constanten
Constante expressies kunnen van het type nint of nuintzijn.
Er is geen directe syntaxis voor inheemse int-literals. Impliciete of expliciete casts van andere integrale constante waarden kunnen in plaats daarvan worden gebruikt: const nint i = (nint)42;.
nint constanten zijn in het bereik [ int.MinValue, int.MaxValue ].
nuint constanten zijn in het bereik [ uint.MinValue, uint.MaxValue ].
Er zijn geen MinValue- of MaxValue velden in nint of nuint, omdat deze waarden, behalve nuint.MinValue, niet als constanten kunnen worden verzonden.
Constant vouwen wordt ondersteund voor alle unaire operatoren { +, -, ~ } en binaire operatoren { +, -, *, /, %, ==, !=, <, <=, >, >=, &, |, ^, <<, >> }.
Constante vouwbewerkingen worden geëvalueerd met Int32 en UInt32 operanden in plaats van systeemeigen ints voor consistent gedrag, ongeacht het compilerplatform.
Als de bewerking resulteert in een constante waarde van 32-bits, wordt constante vouwing uitgevoerd tijdens de compilatie.
Anders wordt de bewerking tijdens runtime uitgevoerd en wordt deze niet beschouwd als een constante.
Conversies
Er is een identiteitsconversie tussen nint en IntPtr, en tussen nuint en UIntPtr.
Er is een identiteitsconversie tussen samengestelde typen die alleen verschillen door native ints en onderliggende typen: arrays, Nullable<>, samengestelde typen en tuples.
De onderstaande tabellen hebben betrekking op de conversies tussen speciale typen.
(De IL voor elke conversie bevat de varianten voor unchecked en checked contexten als ze verschillend zijn.)
Algemene opmerkingen in de onderstaande tabel:
-
conv.uis een nul-uitbreidende conversie naar een systeemeigen geheel getal enconv.iis een teken-uitbreidende conversie naar een systeemeigen geheel getal. -
checkedcontexten voor zowel verruimende als beperkende zijn:-
conv.ovf.*voorsigned to * -
conv.ovf.*.unvoorunsigned to *
-
-
uncheckedcontexten voor breder maken zijn:-
conv.i*voorsigned to *(waarbij * de doelbreedte is) -
conv.u*voorunsigned to *(waarbij * de doelbreedte is)
-
-
uncheckedcontexten voor het beperken van zijn:-
conv.i*voorany to signed *(waarbij * de doelbreedte is) -
conv.u*voorany to unsigned *(waarbij * de doelbreedte is)
-
Enkele voorbeelden:
-
sbyte to nintensbyte to nuintconv.igebruiken terwijlbyte to nintenbyte to nuintconv.ugebruiken, omdat ze allemaal breder worden. -
nint to byteennuint to byteconv.u1gebruiken terwijlnint to sbyteennuint to sbyteconv.i1gebruiken. Voorbyte,sbyte,shortenushortis het 'stacktype'int32.conv.i1betekent effectief 'een signed byte maken en vervolgens voortzetten tot int32', terwijlconv.u1betekent 'een unsigned byte maken en vervolgens nul-uitbreiden tot int32'. -
checked void* to nintgebruiktconv.ovf.i.unop dezelfde manier alschecked void* to longconv.ovf.i8.ungebruikt.
| Operand | Doel | Conversie | IL |
|---|---|---|---|
object |
nint |
Uitpakken | 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 |
Identiteit | |
UIntPtr |
nint |
Geen | |
object |
nuint |
Uitpakken | 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 |
Geen | |
UIntPtr |
nuint |
Identiteit | |
| Opsomming | nint |
ExplicieteEnumeratie | |
| Opsomming | nuint |
ExplicieteEnumeratie |
| Operand | Doel | Conversie | IL |
|---|---|---|---|
nint |
object |
Boksen | box |
nint |
void* |
PointerToVoid | Nop/ conv.ovf.u |
nint |
nuint |
ExplicitNumeric |
conv.u (kan worden weggelaten) / 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 |
Identiteit | |
nint |
UIntPtr |
Geen | |
nint |
Opsomming | ExplicieteEnumeratie | |
nuint |
object |
Boksen | box |
nuint |
void* |
PointerToVoid | Nop |
nuint |
nint |
ExplicitNumeric |
conv.i(kan worden weggelaten) / 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 |
Geen | |
nuint |
UIntPtr |
Identiteit | |
nuint |
Opsomming | ExplicieteEnumeratie |
Conversie van A naar Nullable<B> is:
- een impliciete nulleerbare conversie als er sprake is van een identiteitsconversie of een impliciete conversie van
AnaarB; - een expliciete nulconversie als er een expliciete omzetting is van
AnaarB; - anders ongeldig.
Conversie van Nullable<A> naar B is:
- een expliciete nullbare conversie als er een identiteitsconversie of impliciete of expliciete numerieke conversie van
AnaarBis; - anders ongeldig.
Conversie van Nullable<A> naar Nullable<B> is:
- een identiteitsconversie als er een identiteitsconversie is van
AnaarB; - een expliciete nullable conversie als er een impliciete of expliciete numerieke conversie van
AnaarBis; - anders ongeldig.
Bedieners
De vooraf gedefinieerde operators zijn als volgt.
Deze operators worden tijdens overbelastingsresolutie beschouwd op basis van normale regels voor impliciete conversies als ten minste één van de operanden van het type nint of nuintis.
(De IL bevat de varianten voor elk operator voor de unchecked- en checked-contexten indien verschillend.)
| Unaire | Operatorhandtekening | 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 |
| Binair | Operatorhandtekening | 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 |
Voor sommige binaire operators ondersteunen de IL-operators aanvullende operandtypen (zie ECMA-335 III.1.5 Operand type tabel). Maar de set operandtypen die door C# worden ondersteund, is beperkt voor eenvoud en voor consistentie met bestaande operators in de taal.
Opgeheven versies van de operators, waarbij de argumenten en retourtypen worden nint? en nuint?, worden ondersteund.
Samengestelde toewijzingsbewerkingen x op= y waarbij x of y systeemeigen ints zijn, voldoen aan dezelfde regels als bij andere primitieve typen met vooraf gedefinieerde operators.
De expressie is specifiek gebonden als x = (T)(x op y) waarbij T het type x is en waarbij x slechts eenmaal wordt geëvalueerd.
De shiftoperators moeten het aantal bits maskeren dat moet worden verplaatst- naar 5 bits als sizeof(nint) 4 is en tot 6 bits als sizeof(nint) 8 is.
(zie §12.11) in de C#-specificatie).
De C#9-compiler rapporteert fouten die zijn gekoppeld aan vooraf gedefinieerde systeemeigen gehele getallen bij het compileren met een eerdere taalversie, maar maakt het gebruik van vooraf gedefinieerde conversies naar en van systeemeigen gehele getallen mogelijk.
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
}
}
Rekenkundige bewerkingen met pointers
Er zijn geen vooraf gedefinieerde operators in C# voor het optellen of aftrekken van aanwijzers met natuurlijke gehele getallen.
In plaats daarvan worden nint- en nuint-waarden gepromoveerd tot long en ulong, en gebruiken aanwijzerberekeningen vooraf gedefinieerde operatoren voor die typen.
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)
Binaire numerieke promoties
De binaire numerieke promoties, zoals beschreven in de informatieve tekst (zie §12.4.7.3in de C#-specificatie), worden als volgt bijgewerkt:
- …
- Als een operand van het type
ulongis, wordt de andere operand geconverteerd naar het typeulongof treedt er een bindingstijdfout op als de andere operand van het typesbyte,short,int,nintoflongis.- Anders, als een van de operanden van het type
nuintis, wordt de andere operand geconverteerd naar het typenuint, of er treedt een bindingstijdfout op als de andere operand van het typesbyte,short,int,nintoflongis.- Als een van beide operanden van het type
longis, wordt de andere operand geconverteerd naar het typelong.- Als een van beide operanden van het type
uintis en de andere operand van het typesbyte,short,nint, ofint, worden beide operanden geconverteerd naar het typelong.- Als een van beide operanden van het type
uintis, wordt de andere operand geconverteerd naar het typeuint.- Als een operand van het type
nintis, wordt de andere operand geconverteerd naar het typenint.- Anders worden beide operanden geconverteerd naar het type
int.
Dynamisch
De conversies en operators worden gesynthetiseerd door de compiler en maken geen deel uit van de onderliggende IntPtr en UIntPtr typen.
Als gevolg hiervan zijn deze conversies en operators niet beschikbaar van de runtime-binder voor 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'
Typeleden
De enige constructor voor nint of nuint is de constructor met parameterloze waarden.
De volgende leden van System.IntPtr en System.UIntPtrworden expliciet uitgesloten van nint of 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();
De resterende leden van System.IntPtr en System.UIntPtrworden impliciet opgenomen in nint en nuint. Voor .NET Framework 4.7.2:
public override bool Equals(object obj);
public override int GetHashCode();
public override string ToString();
public string ToString(string format);
Interfaces die worden geïmplementeerd door System.IntPtr en System.UIntPtrworden impliciet opgenomen in nint en nuint, waarbij exemplaren van de onderliggende typen worden vervangen door de bijbehorende systeemeigen gehele getallen.
Als IntPtr bijvoorbeeld ISerializable, IEquatable<IntPtr>, IComparable<IntPtr>implementeert, implementeert nintISerializable, IEquatable<nint>, IComparable<nint>.
Overschrijven, verbergen en implementeren
nint en System.IntPtr, en nuint en System.UIntPtr, worden beschouwd als gelijkwaardig voor het overschrijven, verbergen en implementeren.
Overbelastingen kunnen niet verschillen per nint en System.IntPtr, en alleen nuint en System.UIntPtr.
Overschrijvingen en implementaties kunnen alleen verschillen per nint en System.IntPtr, of nuint en System.UIntPtr.
Methoden verbergen andere methoden die verschillen per nint en System.IntPtr, of alleen nuint en System.UIntPtr.
Allerlei
nint en nuint expressies die worden gebruikt als matrixindexen, worden zonder conversie verzonden.
static object GetItem(object[] array, nint index)
{
return array[index]; // ok
}
nint en nuint kunnen niet worden gebruikt als een enum basistype van C#.
enum E : nint // error: byte, sbyte, short, ushort, int, uint, long, or ulong expected
{
}
Lees- en schrijfbewerkingen zijn atomisch voor nint en nuint.
Velden kunnen worden gemarkeerd als volatile voor typen nint en nuint.
ECMA-334 15.5.4 bevat echter geen enum met basistype System.IntPtr of System.UIntPtr.
default(nint) en new nint() gelijk zijn aan (nint)0; default(nuint) en new nuint() zijn gelijk aan (nuint)0.
typeof(nint)is ; typeof(nuint) is typeof(UIntPtr)typeof(IntPtr).
sizeof(nint) en sizeof(nuint) worden ondersteund, maar vereisen compilatie in een onveilige context (zoals vereist voor sizeof(IntPtr) en sizeof(UIntPtr)).
De waarden zijn geen compileertijdconstanten.
sizeof(nint) wordt geïmplementeerd als sizeof(IntPtr) in plaats van IntPtr.Size; sizeof(nuint) wordt geïmplementeerd als sizeof(UIntPtr) in plaats van UIntPtr.Size.
Compilerdiagnostiek voor typeverwijzingen met betrekking tot nint of nuint rapporteert nint of nuint in plaats van IntPtr of UIntPtr.
Metagegevens
nint en nuint worden weergegeven in metagegevens als System.IntPtr en System.UIntPtr.
Typeverwijzingen die nint of nuint bevatten, worden verzonden met een System.Runtime.CompilerServices.NativeIntegerAttribute om aan te geven welke delen van de typeverwijzing native ints zijn.
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;
}
}
De codering van typeverwijzingen met NativeIntegerAttribute wordt behandeld in NativeIntegerAttribute.md.
Alternatieven
Een alternatief voor de bovenstaande methode voor 'typeverwijdering' is het introduceren van nieuwe typen: System.NativeInt en System.NativeUInt.
public readonly struct NativeInt
{
public IntPtr Value;
}
Verschillende typen zouden overbelastingen mogelijk maken die verschillen van IntPtr en zouden afzonderlijke parsering en ToString()toestaan.
Maar er zou meer werk zijn voor de CLR om deze typen efficiënt te verwerken, waardoor het primaire doel van de functie - efficiëntie wordt verslagen.
En interop met bestaande systeemeigen int-code die gebruikmaakt van IntPtr zou moeilijker zijn.
Een ander alternatief is om meer systeemeigen int-ondersteuning toe te voegen voor IntPtr in het framework, maar zonder specifieke compilerondersteuning.
Nieuwe conversies en rekenkundige bewerkingen worden automatisch ondersteund door de compiler.
Maar de taal biedt geen trefwoorden, constanten of checked bewerkingen.
Ontwerpvergaderingen
- 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