Поделиться через


Числовой IntPtr

Заметка

Эта статья является спецификацией компонентов. Спецификация служит проектным документом для функции. Она включает предлагаемые изменения спецификации, а также информацию, необходимую во время проектирования и разработки функции. Эти статьи публикуются до тех пор, пока предложенные изменения спецификации не будут завершены и включены в текущую спецификацию ECMA.

Может возникнуть некоторое несоответствие между спецификацией компонентов и завершенной реализацией. Эти различия отражены в соответствующих заседаниях, посвящённых проектированию языка (LDM).

Дополнительные сведения о процессе внедрения спецификаций функций в стандарт языка C# см. в статье о спецификациях .

Выпуск чемпиона: https://github.com/dotnet/csharplang/issues/6065

Сводка

Это редакция функции начальных собственных целых чисел (спецификации), где типы nint/nuint отличались от базовых типов System.IntPtr/System.UIntPtr. Короче говоря, теперь мы рассматриваем nint/nuint как псевдонимы простых типов System.IntPtr/System.UIntPtr, подобно тому как мы делаем для int относительно System.Int32. Флаг функции среды выполнения System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr активирует это новое поведение.

Дизайн

8.3.5 Простые типы

C# предоставляет набор стандартных struct типов, называемых простыми типами. Простые типы определяются с помощью ключевых слов, но эти ключевые слова являются просто псевдонимами для предопределенных типов struct в пространстве имен System, как описано в таблице ниже.

Ключевое слово Псевдоним типа
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 Целочисленные типы

C# поддерживает одиннадцать целочисленных типов: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulongи char. [...]

8.8 Неуправляемые типы

Другими словами, unmanaged_type является одним из следующих:

  • sbyte, byte, shortushort, int, uint, nint, nuint, long, ulong, char, float, double, decimalили bool.
  • Любая enum_type.
  • Любая определяемая пользователем структура типа, которая не является сконструированным типом и содержит только поля типа unmanaged_type.
  • В небезопасном коде любые pointer_type.

10.2.3 Неявные числовые преобразования

Неявные числовые преобразования:

  • От sbyte до short, int, nint, long, float, doubleили decimal.
  • От byte до short, ushort, int, uint, nint, nuint, long, ulong, floatdoubleили decimal.
  • От short до int, nint, long, float, doubleили decimal.
  • От ushort до int, uint, nint, nuint, long, ulong, float, doubleили decimal.
  • От int до nint, long, float, doubleили decimal.
  • От uint до nuint, long, ulong, float, doubleили decimal.
  • от nint до long, float, doubleили decimal.
  • от nuint до ulong, float, doubleили decimal.
  • От long до float, doubleили decimal.
  • От ulong до float, doubleили decimal.
  • От char до ushort, int, uint, nint, nuint, long, ulong, float, doubleили decimal.
  • От float до double.

[...]

Преобразования неявных константных выражений 10.2.11

Неявное преобразование константного выражения позволяет выполнить следующие преобразования:

  • constant_expression типа int можно преобразовать в тип sbyte, byte, short, ushort, uint, nint, nuintили ulong, если значение constant_expression находится в диапазоне целевого типа. [...]

10.3.2 Явные числовые преобразования

Явные числовые преобразования — это преобразования из numeric_type в другую numeric_type, для которой неявное числовое преобразование еще не существует:

  • От sbyte до byte, ushort, uint, nuint, ulongили char.
  • От byte до sbyte или char.
  • От short до sbyte, byte, ushort, uint, nuint, ulongили char.
  • От ushort до sbyte, byte, shortили char.
  • От int до sbyte, byte, short, ushort, uint, nuint, ulongили char.
  • От uint до sbyte, byte, short, ushort, int, nintили char.
  • От long до sbyte, byte, short, ushort, int, uint, nint, nuint, ulongили char.
  • от nint до sbyte, byte, short, ushort, int, uint, nuint, ulongили char.
  • от nuint до sbyte, byte, short, ushort, int, uint, nint, longили char.
  • От ulong до sbyte, byte, short, ushort, int, uint, nint, nuint, longили char.
  • От char до sbyte, byteили short.
  • От float до sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, charили decimal.
  • От double до sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, floatили decimal.
  • От decimal до sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, floatили double.

[...]

10.3.3 Явные преобразования типов перечисления

Конкретные преобразования перечисления:

  • От sbyte, byte, short, ushort, int, uint, ,nint, nuint,, long, ulong, char, float, doubleили decimal в любое enum_type.
  • От любого enum_type до sbyte, byte, short, ushort, int, uint, nint, nuintlong, ulong, char, float, doubleили decimal.
  • От любого enum_type до любого другого enum_type.

12.6.4.7 Лучший целевой объект преобразования

Учитывая два типа T₁ и T₂, T₁ — это лучший целевой объект преобразования, чем T₂, если выполняется одно из следующих условий:

  • Неявное преобразование из T₁ в T₂ существует и неявное преобразование из T₂ в T₁ не существует.
  • T₁ - это Task<S₁>, T₂ - это Task<S₂>, и S₁ является более подходящей целью для преобразования, чем S₂
  • T₁ S₁ или S₁?, где S₁ является подписанным целочисленным типом, T₂S₂ или S₂?, где S₂ является целочисленным типом без знака. В частности: [...]

12.8.12 Доступ к элементу

[...] Число выражений в argument_list должно совпадать с рангом array_type, а каждое выражение должно быть типом int, uint, nint, nuint, longили ulong, или неявно преобразовано в один или несколько этих типов.

Доступ к массиву 11.8.12.2

[...] Число выражений в argument_list должно совпадать с рангом array_type, а каждое выражение должно быть типом int, uint, nint, nuint, longили ulong, или неявно преобразовано в один или несколько этих типов.

[...] Обработка обращения к массиву формы P[A], где P является primary_no_array_creation_expression типа array_type, а A является argument_list, состоит из следующих шагов: [...]

  • Выражения индекса argument_list вычисляются по порядку слева направо. После оценки каждого выражения индекса выполняется неявное преобразование в один из следующих типов: int, uint, nint, nuint, long, ulong. Первый тип в этом списке, для которого существует неявное преобразование, выбирается. [...]

12.8.16 Операторы постфиксного инкремента и декремента

Разрешение перегрузки унарного оператора применяется для выбора конкретной реализации оператора. Стандартные операторы ++ и -- существуют для следующих типов: sbyte, byte, short, ushort, int, uint, ,nint, nuint,,long, ulong, char, float, double, decimalи любого типа перечисления.

Оператор 12.9.2 Unary plus

Стандартные унарные операторы плюс:

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

12.9.3 Оператор унарного минуса

Стандартные унарные операторы минус:

  • Целочисленное отрицание:

    ...
    nint operator –(nint x);
    

12.8.16 Операторы постфиксного инкремента и декремента

Стандартные операторы ++ и -- существуют для следующих типов: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimalи любого типа перечисления.

Выражения значений по умолчанию 11.7.19

Кроме того, default_value_expression является константным выражением, если тип является одним из следующих типов значений: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal, bool, или любого типа перечисления.

Оператор дополнения 12.9.5 Bitwise

Стандартные операторы битового дополнения:

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

12.9.6 Префиксные инкрементные и декрементные операторы

Стандартные операторы ++ и -- существуют для следующих типов: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimalи любого типа перечисления.

12.10 Арифметические операторы

Оператор умножения 12.10.2

Ниже перечислены предопределенные операторы умножения. Все операторы вычисляют продукт x и y.

  • Целочисленное умножение:

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

12.10.3 Оператор деления

Ниже перечислены предопределенные операторы деления. Все операторы вычисляют частное x на y.

  • Целочисленное деление

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

Оператор остатка 12.10.4

Ниже перечислены предопределенные операторы остатка. Все операторы вычисляют оставшуюся часть деления между x и y.

  • Остаток целочисленного числа:

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

Оператор сложения 12.10.5

  • Добавление целых чисел:

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

12.10.6 Оператор вычитания

  • Вычитание целочисленного числа:

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

Операторы сдвига 12.11

Ниже перечислены предопределенные операторы смены.

  • Сдвиг влево

    ...
    nint operator <<(nint x, int count);
    nuint operator <<(nuint x, int count);
    
  • Сдвиг вправо:

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

    Оператор >> сдвигает x вправо на несколько битов, вычисляемых, как описано ниже.

    Если x имеет тип int, nint или long, то младшие биты x удаляются, оставшиеся биты сдвигаются вправо, а старшие пустые позиции устанавливаются в ноль, если x не является отрицательным, и устанавливаются в единицу, если x отрицательное.

    Если x имеет тип uint, nuint или ulong, то биты с низким порядком x удаляются, остальные биты сдвигаются вправо, а пустые позиции с высоким порядком пустых битов равны нулю.

  • Беззнаковый сдвиг вправо:

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

Для предопределенных операторов число битов для смены вычисляется следующим образом: [...]

  • Если тип xnint или nuint, число сдвигов определяется пятью младшими битами count на 32-разрядной платформе или шестью младшими битами count на 64-разрядной платформе.

Операторы реляционного и типового тестирования 12.12

Операторы сравнения целых чисел 12.12.2

Стандартные операторы сравнения целых чисел:

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

12.12.2 Целочисленные логические операторы

Стандартные логические операторы целого числа:

...
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 Константные выражения

Константное выражение может быть либо типом значения, либо ссылочным типом. Если константное выражение является типом значения, оно должно быть одним из следующих типов: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal, bool, или любого типа перечисления.

[...]

Преобразование неявного константного выражения позволяет преобразовать константное выражение типа int в sbyte, byte, short, ushort, uint, nint, nuint или ulong, если значение константного выражения находится в диапазоне конечного типа.

Доступ к элементу массива 17.4

Доступ к элементам массива осуществляется с использованием element_access выражений в форме A[I₁, I₂, ..., Iₓ], где A является выражением типа массива, а каждое Iₑ — выражение типа int, uint, nint, nuint,long, ulongили может быть неявно преобразовано в один или несколько этих типов. Результатом доступа к элементу массива является переменная, а именно элемент массива, выбранный индексами.

23.5 Преобразования указателей

23.5.1 Общие

[...]

Кроме того, в небезопасном контексте набор доступных явных преобразований расширяется, чтобы включить следующие явные преобразования указателя:

  • От любого указателя типа к любому другому указателю типа.
  • От sbyte, byte, short, ushort, int, uint, ,nint, nuint,longили ulong в любой pointer_type.
  • От любого pointer_type до sbyte, byte, short, ushort, int, uint, nint, nuint,longили ulong.

Доступ к элементу указателя 23.6.4

[...] В доступе к элементу формы P[E]P должен быть выражением типа указателя, отличного от void*, и E должно быть выражением, которое может быть неявно преобразовано в int, uint, nint, nuint,longили ulong.

23.6.7 Арифметика указателей

В небезопасном контексте оператор + и можно применять к значениям всех типов указателей, кроме void*. Таким образом, для каждого типа указателя T*неявно определены следующие операторы:

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

Учитывая выражение P типа указателя T* и выражение N типа int, uint, nint, nuint,longили ulong, выражения P + N и N + P вычисляют значение указателя типа T*, которое приводит к добавлению N * sizeof(T) в адрес, заданный P. Аналогичным образом, выражение P – N вычисляет значение указателя типа T*, которое получается в результате вычитания N * sizeof(T) из адреса, заданного P.

Различные соображения

Серьезные изменения

Одним из основных последствий этого дизайна является то, что System.IntPtr и System.UIntPtr получают некоторые встроенные операторы (преобразования, унарные и двоичные).
К ним относятся операторы checked, что означает, что следующие операторы для этих типов теперь будут вызываться при переполнении:

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

Кодировка метаданных

Это проектирование означает, что nint и nuint могут быть просто переданы как System.IntPtr и System.UIntPtr, без использования System.Runtime.CompilerServices.NativeIntegerAttribute.
Аналогичным образом, при загрузке метаданных NativeIntegerAttribute следует игнорировать.