Freigeben über


Numerisches IntPtr

Hinweis

Dieser Artikel ist eine Feature-Spezifikation. Die Spezifikation dient als Designdokument für das Feature. Es enthält vorgeschlagene Spezifikationsänderungen sowie Informationen, die während des Entwurfs und der Entwicklung des Features erforderlich sind. Diese Artikel werden veröffentlicht, bis die vorgeschlagenen Spezifikationsänderungen abgeschlossen und in die aktuelle ECMA-Spezifikation aufgenommen werden.

Es kann einige Abweichungen zwischen der Feature-Spezifikation und der abgeschlossenen Implementierung geben. Diese Unterschiede werden in den relevanten Anmerkungen zum Language Design Meeting (LDM) erfasst.

Weitere Informationen zur Einführung von Funktionen in den C#-Sprachstandard finden Sie im Artikel zu den Spezifikationen.

Champion-Problem: https://github.com/dotnet/csharplang/issues/6065

Zusammenfassung

Dies ist eine Änderung der ursprünglichen nativen Ganzzahlfunktion (Spezifikation), in der sich die nint/nuint-Typen von den zugrunde liegenden Typen System.IntPtr/System.UIntPtr unterschieden haben. Kurz gesagt behandeln wir nint/nuint als einfache Typenaliase für System.IntPtr/System.UIntPtr, wie bei int in Bezug auf System.Int32. Das Laufzeitfunktions-Flag System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr löst dieses neue Verhalten aus.

Design

8.3.5 Einfache Typen

C# stellt eine Reihe vordefinierter struct-Typen bereit, die einfache Typen genannt werden. Die einfachen Typen werden durch Schlüsselwörter identifiziert. Diese Schlüsselwörter sind jedoch einfach Aliase für vordefinierte struct-Typen im System-Namespace, wie in der folgenden Tabelle beschrieben.

Schlüsselwort 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 Integrale Typen

C# unterstützt elf integrale Typen: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong und char. [...]

8.8 Nicht verwaltete Typen

Anders ausgedrückt bedeutet dies, dass es sich bei einem unmanaged_type um einen der folgenden Typen handelt:

  • sbyte, byte, short, ushort, int, uint, ,nint, nuint,, long, ulong, char, float, double, decimaloder bool.
  • Ein enum_type.
  • Ein benutzerdefinierter struct_type, der nicht konstruiert ist und nur unmanaged_type-Felder enthält.
  • In unsicherem Code ein .

10.2.3 Implizite numerische Konvertierungen

Die impliziten numerischen Konvertierungen sind:

  • Von sbyte in short, int, nint, long, float, double oder decimal.
  • Von byte in short, ushort, int, uint, nint, nuint, long, ulong, float, double oder decimal.
  • Von short in int, nint, long, float, double oder decimal.
  • Von ushort in int, uint, nint, nuint, long, ulong, float, double oder decimal.
  • Von int in nint, long, float, double oder decimal.
  • Von uint in nuint, long, ulong, float, double oder decimal.
  • Von nint in long, float, double oder decimal.
  • Von nuint in ulong, float, double oder decimal.
  • Von long in float, double oder decimal.
  • Von ulong in float, double oder decimal.
  • Von char in ushort, int, uint, nint, nuint, long, ulong, float, double oder decimal.
  • Von float bis double.

[...]

10.2.11 Implizite Konvertierungen von Konstantenausdrücken

Eine implizite Konvertierung von Konstantenausdrücken ermöglicht die folgenden Konvertierungen:

  • Ein constant_expression mit dem Typ int kann in den Typ sbyte, byte, short, ushort, uint, nint, nuint oder ulong konvertiert werden, wenn der Wert von constant_expression innerhalb des Bereichs des Zieltyps liegt. [...]

10.3.2 Explizite numerische Konvertierungen

Die expliziten numerischen Konvertierungen sind Konvertierungen aus einem numeric_type in einen anderen numeric_type, für die noch keine implizite numerische Konvertierung vorhanden ist:

  • Von sbyte in byte, ushort, uint, nuint, ulong oder char.
  • Von byte in sbyte oder char.
  • Von short in sbyte, byte, ushort, uint, nuint, ulong oder char.
  • Von ushort in sbyte, byte, short oder char.
  • Von int in sbyte, byte, short, ushort, uint, nuint, ulong oder char.
  • Von uint in sbyte, byte, short, ushort, int, nint oder char.
  • Von long in sbyte, byte, short, ushort, int, uint, nint, nuint, ulong oder char.
  • Von nint in sbyte, byte, short, ushort, int, uint, nuint, ulong oder char.
  • Von nuint in sbyte, byte, short, ushort, int, uint, nint, long oder char.
  • Von ulong in sbyte, byte, short, ushort, int, uint, nint, nuint, long oder char.
  • Von char in sbyte, byte oder short.
  • Von float in sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char oder decimal.
  • Von double in sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float oder decimal.
  • Von decimal in sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float oder double.

[...]

10.3.3 Explizite Enumerationskonvertierungen

Die expliziten Enumerationskonvertierungen sind:

  • Von sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double oder decimal in einen enum_type.
  • Von einem enum_type in sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double oder decimal.
  • Von einem enum_type in einen anderen enum_type.

12.6.4.7 Besseres Konvertierungsziel

Bei den beiden Typen T₁ und T₂ ist T₁ ein besseres Konvertierungsziel als T₂, wenn eine der folgenden Bedingungen erfüllt ist:

  • Eine implizite Konversion von T₁ nach T₂ existiert und keine implizite Konversion von T₂ nach T₁ existiert
  • T₁ ist Task<S₁>, T₂ ist Task<S₂>, und S₁ ist ein besseres Konvertierungsziel als S₂
  • T₁ ist S₁ oder S₁?, wobei S₁ ein Integraltypen mit Vorzeichen ist, und T₂ ist S₂ oder S₂?, wobei S₂ ein Integraltypen ohne Vorzeichen ist. Insbesondere: [...]

12.8.12 Elementzugriff

[...] Die Anzahl der Ausdrücke in der argument_list muss dem Rang des array_type entsprechen und jeder Ausdruck muss vom Typ int, uint, nint, nuint, long oder ulong, sein oder implizit in einen oder mehrere dieser Typen konvertierbar sein.

11.8.12.2 Arrayzugriff

[...] Die Anzahl der Ausdrücke in der argument_list muss dem Rang des array_type entsprechen und jeder Ausdruck muss vom Typ int, uint, nint, nuint, long oder ulong, sein oder implizit in einen oder mehrere dieser Typen konvertierbar sein.

[...] Die Laufzeitverarbeitung eines Arrayzugriffs der Form P[A], wobei P ein primary_no_array_creation_expression eines array_type ist und A eine argument_list ist, umfasst die folgenden Schritte: [...]

  • Die Indexausdrücke der argument_list werden von links nach rechts ausgewertet. Nach der Auswertung jedes Indexausdrucks erfolgt eine implizite Konvertierung in einen der folgenden Typen: int, uint, nint, nuint, long, ulong. Der erste Typ in dieser Liste, für den eine implizite Konversion existiert, wird ausgewählt. [...]

12.8.16 Inkrementierungs- und Dekrementierungsoperatoren in Postfixnotation

Die unäre Operatorüberladungsauflösung wird angewendet, um eine bestimmte Operatorimplementierung auszuwählen. Es gibt vordefinierte ++- und ---Operatoren für die folgenden Typen: sbyte, byte, short, ushort, int, uint, nint, nuint,long, ulong, char, float, double, decimal und alle Enumerationstypen.

12.9.2 Unärer Plus-Operator

Die vordefinierten unären Plus-Operatoren sind:

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

12.9.3 Unärer Minus-Operator

Die vordefinierten unären Minus-Operatoren sind:

  • Ganzzahlnegation:

    ...
    nint operator –(nint x);
    

12.8.16 Inkrementierungs- und Dekrementierungsoperatoren in Postfixnotation

Es gibt vordefinierte ++- und ---Operatoren für die folgenden Typen: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal und alle Enumerationstypen.

11.7.19 Ausdrücke für Standardwerte

Darüber hinaus ist ein default_value_expression ein konstanter Ausdruck, wenn der Typ einer der folgenden Werttypen ist: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal, bool, oder ein Enumerationstyp.

12.9.5 Bitweiser Komplement-Operator

Die vordefinierten bitweisen Komplement-Operatoren sind:

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

12.9.6 Inkrementierungs- und Dekrementierungsoperatoren in Präfixnotation

Es gibt vordefinierte ++- und ---Operatoren für die folgenden Typen: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal und alle Enumerationstypen.

12.10 Arithmetische Operatoren

12.10.2 Multiplikations-Operator

Die vordefinierten Multiplikationsoperatoren sind im Folgenden aufgeführt. Die Operatoren berechnen alle das Produkt von x und y.

  • Ganzzahlmultiplikation:

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

12.10.3 Divisions-Operator

Die vordefinierten Divisionsoperatoren sind unten aufgeführt. Alle Operatoren berechnen den Quotienten von x und y.

  • Ganzzahldivision:

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

12.10.4 Rest-Operator

Die vordefinierten Rest-Operatoren werden unten aufgeführt. Alle Operatoren berechnen den Rest der Division zwischen x und y.

  • Ganzzahlrest:

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

12.10.5 Additions-Operator

  • Ganzzahladdition:

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

12.10.6 Subtraktions-Operator

  • Ganzzahlsubtraktion:

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

12.11 Schiebeoperatoren

Die vordefinierten Verschiebungsoperatoren sind unten aufgeführt.

  • Nach links verschieben:

    ...
    nint operator <<(nint x, int count);
    nuint operator <<(nuint x, int count);
    
  • Nach rechts verschieben:

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

    Der >>-Operator verschiebt x um eine Anzahl von Bits nach rechts, die wie unten beschrieben berechnet wird.

    Wenn x vom Typ int, nint oder longist, werden die Bits mit niedriger Reihenfolge von x verworfen, die verbleibenden Bits werden nach rechts verschoben, und die leeren Bitpositionen in hoher Reihenfolge werden auf Null festgelegt, wenn x nicht negativ ist, und auf Eins festgelegt, wenn x negativ ist.

    Wenn x vom Typ uint, nuint oder ulongist, werden die niederwertigsten Bits von x verworfen, die verbleibenden Bits werden nach rechts verschoben, und die höchstwertigen leeren Bitpositionen werden auf Null gesetzt.

  • Vorzeichenlose Rechtsverschiebung:

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

Für die vordefinierten Operatoren wird die Anzahl der zu verschiebenden Bits wie folgt berechnet: [...]

  • Wenn der Typ von xnint oder nuint ist, wird der Verschiebungswert durch die niederwertigen fünf Bits von count auf einer 32-Bit-Plattform oder die niederwertigen sechs Bits von count auf einer 64-Bit-Plattform angegeben.

12.12 Relationale und Typtest-Operatoren

12.12.2 Ganzzahlvergleichs-Operatoren

Die vordefinierten ganzzahligen Vergleichsoperatoren sind:

...
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 Logische Operatoren

12.12.2 Logische Ganzzahl-Operatoren

Die vordefinierten ganzzahligen logischen Operatoren sind:

...
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 Konstante Ausdrücke

Ein konstanter Ausdruck kann ein Werttyp oder ein Referenztyp sein. Wenn ein Konstantenausdruck ein Werttyp ist, muss es sich um einen der folgenden Typen handeln: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal, bool, oder einen Enumerationstyp.

[...]

Eine implizite Konversion von konstanten Ausdrücken erlaubt die Konvertierung eines konstanten Ausdrucks vom Typ int in sbyte, byte, short, ushort, uint, nint, nuint, oder ulong, sofern der Wert des konstanten Ausdrucks im Bereich des Zieltyps liegt.

17.4 Zugriff auf Arrayelemente

Auf Arrayelemente wird mithilfe von element_access Ausdrücken im Format A[I₁, I₂, ..., Iₓ] zugegriffen, wobei A ein Ausdruck eines Arraytyps ist und Iₑ jeweils ein Ausdruck mit dem Typ int, uint, nint, nuint,long oder ulong ist oder implizit in einen oder mehrere dieser Typen konvertiert werden kann. Das Ergebnis eines Zugriffs auf Arrayelemente ist eine Variable, und zwar das Arrayelement, das durch die Indizes ausgewählt wurde.

23.5 Zeigerkonvertierungen

23.5.1 Allgemein

[...]

Darüber hinaus wird der Satz verfügbarer expliziter Konvertierungen in einem unsicheren Kontext erweitert, sodass er die folgenden expliziten Zeigerkonvertierungen einschließt:

  • Von einem pointer_type in einen anderen pointer_type.
  • Von sbyte, byte, short, ushort, int, uint, nint, nuint,long oder ulong in einen pointer_type.
  • Von einem pointer_type in sbyte, byte, short, ushort, int, uint, nint, nuint,long oder ulong.

23.6.5. Zeigerelementzugriff

[...] Bei einem Zeigerelementzugriff auf das Formular P[E] muss P ein Ausdruck eines anderen Zeigertyps als void* sein und E muss ein Ausdruck sein, der implizit in int, uint, nint, nuint,long oder ulong konvertiert werden kann.

23.6.7 Zeigerarithmetik

In einem unsicheren Kontext können der +-Operator und der -Operator auf Werte aller Zeigertypen angewendet werden, mit Ausnahme von void*. Daher werden für jeden Zeigertyp T* die folgenden Operatoren implizit definiert:

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

Auf der Basis eines Ausdrucks P eines Zeigertyps T* und eines Ausdrucks N mit dem Typ int, uint, nint, nuint,long oder ulong berechnen die Ausdrücke P + N und N + P den Zeigerwert des Typs T*, der das Ergebnis der Addition von N * sizeof(T) zu der Adresse ist, die von P angegeben wird. Ähnlich berechnet der Ausdruck P – N den Zeigerwert des Typs T*, der das Ergebnis der Subtraktion von N * sizeof(T) von der Adresse ist, die von P angegeben wird.

Verschiedene Überlegungen

Wichtige Änderungen

Eine der wichtigsten Auswirkungen dieses Designs ist, dass System.IntPtr und System.UIntPtr einige integrierte Operatoren (Konvertierungen, unär und binär) erhalten.
Dazu gehören checked-Operatoren, was bedeutet, dass die folgenden Operatoren für diese Typen jetzt im Überlauf ausgelöst werden:

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

Codierung von Metadaten

Dieses Design bedeutet, dass nint und nuint einfach als System.IntPtr und System.UIntPtr ohne Verwendung von System.Runtime.CompilerServices.NativeIntegerAttribute ausgegeben werden können.
Beim Laden von Metadaten kann NativeIntegerAttribute jetzt ignoriert werden.