Udostępnij za pośrednictwem


Liczbowy IntPtr

Notatka

Ten artykuł jest specyfikacją funkcji. Specyfikacja służy jako dokument projektowy dla funkcji. Zawiera proponowane zmiany specyfikacji wraz z informacjami wymaganymi podczas projektowania i opracowywania funkcji. Te artykuły są publikowane do momentu sfinalizowania proponowanych zmian specyfikacji i włączenia ich do obecnej specyfikacji ECMA.

Mogą wystąpić pewne rozbieżności między specyfikacją funkcji a ukończoną implementacją. Te różnice są przechwytywane w odpowiednich spotkania projektowego języka (LDM).

Więcej informacji na temat procesu wdrażania specyfikacji funkcji można znaleźć w standardzie języka C# w artykule dotyczącym specyfikacji .

Streszczenie

Jest to poprawka początkowej funkcji natywnych liczb całkowitych (specyfikacja), gdzie typy nint/nuint były różne od bazowych typów System.IntPtr/System.UIntPtr. Krótko mówiąc, traktujemy nint/nuint jako proste typy aliasujące System.IntPtr/System.UIntPtr, tak jak w przypadku int w odniesieniu do System.Int32. Flaga funkcji środowiska uruchomieniowego System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr wyzwala to nowe zachowanie.

Projekt

8.3.5 Typy proste

Język C# udostępnia zestaw wstępnie zdefiniowanych typów struct nazywanych prostymi typami. Proste typy są identyfikowane za pomocą słów kluczowych, ale te słowa kluczowe są po prostu aliasami dla wstępnie zdefiniowanych typów struct w przestrzeni nazw System, jak opisano w poniższej tabeli.

słowo kluczowe typ z aliasem
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

[...]

Typy całkowite 8.3.6

C# obsługuje jedenaście typów całkowitych: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulongi char. [...]

8.8 Typy niezarządzane

Innymi słowy, unmanaged_type jest jednym z następujących:

  • sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimallub bool.
  • Dowolny enum_type.
  • Każdy zdefiniowany przez użytkownika struct_type, który nie jest typem skonstruowanym i zawiera tylko pola typu unmanaged_type.
  • W niebezpiecznym kodzie dowolny typ_wskaźnika.

10.2.3 Niejawne konwersje liczbowe

Niejawne konwersje liczbowe to:

  • Od sbyte do short, int, nint, long, float, doublelub decimal.
  • Z byte do short, ushort, int, uint, nint, nuint, long, ulong, floatdoublelub decimal.
  • Od short do int, nint, long, float, doublelub decimal.
  • Z ushort do int, uint, nint, nuint, long, ulong, float, doublelub decimal.
  • Od int do nint, long, float, doublelub decimal.
  • Od uint do nuint, long, ulong, float, doublelub decimal.
  • Od nint do long, float, doublelub decimal.
  • Od nuint do ulong, float, doublelub decimal.
  • Z long do float, doublelub decimal.
  • Z ulong do float, doublelub decimal.
  • Od char do ushort, int, uint, nint, nuint, long, ulong, float, doublelub decimal.
  • Z float do double.

[...]

10.2.11 Niejawne konwersje wyrażeń stałych

Niejawna konwersja wyrażenia stałego zezwala na następujące konwersje:

  • constant_expression typu int można przekonwertować na typ sbyte, byte, short, ushort, uint, nint, nuintlub ulong, pod warunkiem, że wartość constant_expression mieści się w zakresie typu docelowego. [...]

10.3.2 Jawne konwersje liczbowe

Jawne konwersje liczbowe to konwersje z numeric_type do innej numeric_type, dla której nie istnieje jeszcze niejawna konwersja liczbowa:

  • Od sbyte do byte, ushort, uint, nuint, ulonglub char.
  • Z byte do sbyte lub char.
  • Od short do sbyte, byte, ushort, uint, nuint, ulonglub char.
  • Z ushort do sbyte, byte, shortlub char.
  • Z int do sbyte, byte, short, ushort, uint, nuint, ulonglub char.
  • Od uint do sbyte, byte, short, ushort, int, nintlub char.
  • Od long do sbyte, byte, short, ushort, int, uint, nint, nuint, ulonglub char.
  • Od nint do sbyte, byte, short, ushort, int, uint, nuint, ulonglub char.
  • Od nuint do sbyte, byte, short, ushort, int, uint, nint, longlub char.
  • Od ulong do sbyte, byte, short, ushort, int, uint, nint, nuint, longlub char.
  • Z char do sbyte, bytelub short.
  • Z float do sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, charlub decimal.
  • Od double do sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, floatlub decimal.
  • Od decimal do sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, floatlub double.

[...]

10.3.3 Jawne konwersje typu wyliczeniowego

Jawne konwersje wyliczenia to:

  • Z sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, doublelub decimal do dowolnego enum_type.
  • Od dowolnego enum_type do sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, doublelub decimal.
  • Z dowolnego enum_type do dowolnego innego enum_type.

12.6.4.7 Lepszy cel konwersji

Biorąc pod uwagę dwa typy T₁ i T₂, T₁ jest lepszym celem konwersji niż T₂, jeśli spełniony jest jeden z następujących warunków:

  • Istnieje niejawna konwersja z T₁ na T₂ i nie istnieje niejawna konwersja z T₂ na T₁
  • T₁ jest Task<S₁>, T₂ jest Task<S₂>, a S₁ jest lepszym celem konwersji niż S₂
  • T₁ jest S₁ lub S₁?, gdzie S₁ jest typem całkowitoliczbowym ze znakiem, a T₂ jest S₂ lub S₂?, gdzie S₂ jest typem całkowitoliczbowym bez znaku. W szczególności: [...]

12.8.12 Dostęp do elementów

[...] Liczba wyrażeń w argument_list jest taka sama jak ranga array_type, a każde wyrażenie jest typu int, uint, nint, nuint, longlub ulong, lub niejawnie konwertowane na co najmniej jeden z tych typów.

11.8.12.2 Dostęp do tablicy

[...] Liczba wyrażeń w argument_list jest taka sama jak ranga array_type, a każde wyrażenie jest typu int, uint, nint, nuint, longlub ulong, lub niejawnie konwertowane na co najmniej jeden z tych typów.

[...] Przetwarzanie w czasie wykonywania dostępu do tablicy formularza P[A], gdzie P jest primary_no_array_creation_expressionarray_type i A jest argument_list, składa się z następujących kroków: [...]

  • Wyrażenia indeksu argument_list są oceniane w kolejności od lewej do prawej. Po przeprowadzeniu oceny każdego wyrażenia indeksu wykonywana jest niejawna konwersja na jeden z następujących typów: int, uint, nint, nuint, long, ulong. Pierwszy typ na tej liście, dla którego istnieje niejawna konwersja, zostaje wybrany. [...]

12.8.16 Operatory przyrostku i dekrementacji

Rozpoznawanie przeciążenia operatora jednoargumentowego jest stosowane w celu wybrania określonej implementacji operatora. Wstępnie zdefiniowane operatory ++ i -- istnieją dla następujących typów: sbyte, byte, short, ushort, int, uint, ,nint, nuint,,long, ulong, char, float, double, decimali dowolny typ wyliczenia.

12.9.2 Operator jednoargumentowego plusa

Wstępnie zdefiniowane jednoargumentowe operatory plus to:

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

12.9.3 Jednoargumentowy operator minus

Wstępnie zdefiniowane unarne operatory minus to:

  • Negacja liczb całkowitych:

    ...
    nint operator –(nint x);
    

12.8.16 Operatory przyrostku i dekrementacji

Wstępnie zdefiniowane operatory ++ i -- istnieją dla następujących typów: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimali dowolny typ wyliczenia.

11.7.19 Wyrażenia wartości domyślnej

Ponadto default_value_expression jest wyrażeniem stałym, jeśli typ jest jednym z następujących typów wartości: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal, bool, lub dowolny typ wyliczenia.

Operator uzupełniania bitowego 12.9.5

Wstępnie zdefiniowane operatory uzupełniania bitowego to:

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

12.9.6 Operatory preinkrementacji i predekrementacji

Wstępnie zdefiniowane operatory ++ i -- istnieją dla następujących typów: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimali dowolny typ wyliczenia.

12.10 Operatory arytmetyczne

Operator mnożenia 12.10.2

Poniżej wymieniono wstępnie zdefiniowane operatory mnożenia. Wszyscy operatorzy obliczają produkt x i y.

  • Mnożenie liczb całkowitych:

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

Operator dzielenia 12.10.3

Poniżej wymieniono wstępnie zdefiniowane operatory dzielenia. Wszystkie operatory obliczają iloraz x i y.

  • Dzielenie liczb całkowitych:

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

12.10.4 Operator reszty

Poniżej wymieniono wstępnie zdefiniowane operatory pozostałe. Operatory obliczają pozostałą część podziału między x a y.

  • Pozostała liczba całkowita:

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

12.10.5 Operator dodawania

  • Dodawanie liczb całkowitych

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

12.10.6 Operator odejmowania

  • Odejmowanie liczb

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

12.11 Operatory przesunięcia

Poniżej wymieniono predefiniowane operatory przesunięcia.

  • Shift w lewo:

    ...
    nint operator <<(nint x, int count);
    nuint operator <<(nuint x, int count);
    
  • Przesuń w prawo:

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

    Operator >> zmienia x w prawo przez kilka bitów obliczonych zgodnie z poniższym opisem.

    Gdy x jest typu int, nint lub long, bity o niskiej kolejności x są odrzucane, pozostałe bity są przesunięte w prawo, a puste pozycje bitów o wysokiej kolejności są ustawione na zero, jeśli x jest nie ujemna i ustawiona na jedną, jeśli x jest ujemna.

    Gdy x jest typu uint, nuint lub ulong, bity o niskiej kolejności x są odrzucane, pozostałe bity są przesunięte w prawo, a puste pozycje bitów o wysokiej kolejności są ustawione na zero.

  • Niepodpisane przesunięcie w prawo:

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

W przypadku wstępnie zdefiniowanych operatorów liczba bitów do przesunięcia jest obliczana w następujący sposób: [...]

  • Gdy typ x jest nint lub nuint, liczba zmian jest podawana przez pięć bitów o niskiej kolejności count na platformie 32-bitowej lub sześć bitów niższej kolejności count na platformie 64-bitowej.

12.12 Operatory relacyjne i testujące typy

12.12.2 Operatory porównania liczb całkowitych

Wstępnie zdefiniowane operatory porównania liczb całkowitych to:

...
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 Operatory logiczne

Operatory logiczne dla liczb całkowitych

Wstępnie zdefiniowane operatory logiczne liczby całkowitej to:

...
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 Wyrażenia stałe

Wyrażenie stałe może być typem wartości lub typem odwołania. Jeśli wyrażenie stałe jest typem wartości, musi być jednym z następujących typów: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal, bool, lub dowolny typ wyliczenia.

[...]

Niejawna konwersja wyrażenia stałego umożliwia konwersję wyrażenia stałego typu int na sbyte, byte, short, ushort, uint, nint, nuint, lub ulong, pod warunkiem, że wartość wyrażenia stałego mieści się w zakresie typu docelowego.

17.4 Dostęp do elementu tablicy

Dostęp do elementów tablicy uzyskuje się przy użyciu wyrażeń element_access formularza A[I₁, I₂, ..., Iₓ], gdzie A jest wyrażeniem typu tablicy, a każdy Iₑ jest wyrażeniem typu int, uint, nint, nuint,long, ulonglub może zostać niejawnie przekonwertowany na co najmniej jeden z tych typów. Wynikiem dostępu do elementu tablicy jest zmienna, czyli element tablicy wybrany przez indeksy.

23.5 Konwersje wskaźników

23.5.1 Ogólne

[...]

Ponadto w niebezpiecznym kontekście zestaw dostępnych jawnych konwersji rozszerza się, aby uwzględniać następujące jawne konwersje wskaźników:

  • Z dowolnego pointer_type do dowolnego pointer_type.
  • Z sbyte, byte, short, ushort, int, uint, ,nint, nuint,,longlub ulong do dowolnego pointer_type.
  • Z dowolnego pointer_type do sbyte, byte, short, ushort, int, uint, nint, nuint,longlub ulong.

23.6.4 Dostęp do elementu wskaźnika

[...] W przypadku dostępu do elementu wskaźnika w formie P[E], P musi być wyrażeniem typu wskaźnika innego niż void*, a E musi być wyrażeniem, które można niejawnie przekonwertować na int, uint, nint, nuint,longlub ulong.

23.6.7 Arytmetyka wskaźnika

W niebezpiecznym kontekście operator + i operator można zastosować do wartości wszystkich typów wskaźników z wyjątkiem void*. W związku z tym dla każdego typu wskaźnika T*następujące operatory są niejawnie zdefiniowane:

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

Biorąc pod uwagę wyrażenie P typu wskaźnika T* i wyrażenie N typu int, uint, nint, nuint,longlub ulong, wyrażenia P + N i N + P obliczają wartość wskaźnika typu T*, która wynika z dodawania N * sizeof(T) do adresu podanego przez P. Podobnie wyrażenie P – N oblicza wartość wskaźnika typu T* wynikającą z odejmowania N * sizeof(T) z adresu podanego przez P.

Różne zagadnienia

Zmiany ingerujące w kompatybilność

Jednym z głównych skutków tego projektu jest to, że System.IntPtr i System.UIntPtr uzyskują pewne wbudowane operatory (konwersje, operatory jednoargumentowe i binarne).
Należą do nich operatory checked, co oznacza, że następujące operatory na tych typach będą teraz zgłaszane podczas przepełnienia:

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

Kodowanie metadanych

Ten projekt oznacza, że nint i nuint można po prostu emitować jako System.IntPtr i System.UIntPtr, bez użycia System.Runtime.CompilerServices.NativeIntegerAttribute.
Podobnie podczas ładowania metadanych można zignorować NativeIntegerAttribute.