Эта статья является спецификацией компонентов. Спецификация служит проектным документом для функции. Она включает предлагаемые изменения спецификации, а также информацию, необходимую на этапе проектирования и разработки функции. Эти статьи публикуются до тех пор, пока предложенные изменения спецификации не будут завершены и включены в текущую спецификацию ECMA.
Может возникнуть некоторое несоответствие между спецификацией компонентов и завершенной реализацией. Эти различия зафиксированы в соответствующих заметках по проектированию языка (LDM).
Дополнительные сведения о процессе внедрения спецификаций функций в стандарт языка C# см. в статье о спецификациях .
Поддержка языковых типов для подписанных и неподписанных целых чисел стандартного размера.
Мотивация заключается в сценариях взаимодействия между системами и предназначена для использования в низкоуровневых библиотеках.
Дизайн
Идентификаторы nint и nuint являются новыми контекстными ключевыми словами, представляющими собственные типы целочисленных чисел со знаком и без знака.
Идентификаторы обрабатываются только как ключевые слова, если поиск имен не находит подходящий результат в данном контексте программы.
nint x = 3;
_ = nint.Equals(x, 3);
Типы nint и nuint представлены базовыми типами System.IntPtr и System.UIntPtr. Компилятор предоставляет дополнительные преобразования и операции для этих типов как для встроенных целых чисел.
Константы
Константные выражения могут быть типами nint или nuint.
Нет прямого синтаксиса для нативных литералов int. Вместо этого можно использовать неявные или явные приведения других целочисленных констант: const nint i = (nint)42;.
Константы nint находятся в диапазоне [ int.MinValue, int.MaxValue ].
Константы nuint находятся в диапазоне [ uint.MinValue, uint.MaxValue ].
В MinValue или MaxValue полей нет nint или nuint, так как, кроме nuint.MinValue, эти значения нельзя выдавать в виде констант.
Константное свертывание поддерживается для всех унарных операторов { +, -, ~ } и двоичных операторов { +, -, *, /, %, ==, !=, <, <=, >, >=, &, |, ^, <<, >> }.
Операции свертывания констант оцениваются с помощью операндов Int32 и UInt32, а не встроенных целых чисел, для обеспечения согласованного поведения независимо от платформы компилятора.
Если операция приводит к постоянному значению в 32-битном формате, константное свертывание выполняется во время компиляции.
В противном случае операция выполняется во время выполнения и не считается константой.
Преобразования
Существует идентификационное преобразование между nint и IntPtr, а также между nuint и UIntPtr.
Существует преобразование идентичности между составными типами, которые различаются только встроенными целыми числами и базовыми типами: массивы, Nullable<>, составные типы и кортежи.
В приведенных ниже таблицах рассматриваются преобразования между специальными типами.
(IL для каждого преобразования включает варианты для unchecked и checked контекстов, если они отличаются.)
Общие заметки в таблице ниже:
conv.u является преобразованием с нулевым расширением в нативное целое число, а conv.i — преобразованием с знаковым расширением в нативное целое число.
checked контексты для расширения и сужения:
conv.ovf.* для signed to *
conv.ovf.*.un для unsigned to *
Контексты unchecked для расширения :
conv.i* для signed to * (где * — целевая ширина)
conv.u* для unsigned to * (где * — целевая ширина)
unchecked условия для сужения :
conv.i* для any to signed * (где * — целевая ширина)
conv.u* для any to unsigned * (где * — целевая ширина)
Примеры:
sbyte to nint и sbyte to nuint используют conv.i, в то время как byte to nint и byte to nuint используют conv.u, так как они все расширяют.
nint to byte и nuint to byte используют conv.u1, а nint to sbyte и nuint to sbyte используют conv.i1. Для byte, sbyte, shortи ushort тип стека является int32. Таким образом, conv.i1 фактически преобразуется в знаковый байт и затем расширяется до int32, в то время как conv.u1 фактически преобразуется в беззнаковый байт и затем расширяется с нулями до int32.
checked void* to nint использует conv.ovf.i.un так же, как checked void* to long использует conv.ovf.i8.un.
Операнд
Цель
Превращение
IL
object
nint
Распаковки
unbox
void*
nint
УказательНаVoid
nop / conv.ovf.i.un
sbyte
nint
НеявныйЧисловой
conv.i
byte
nint
НеявныйЧисловой
conv.u
short
nint
НеявныйЧисловой
conv.i
ushort
nint
НеявныйЧисловой
conv.u
int
nint
НеявныйЧисловой
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
НеявныйЧисловой
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
неявное преобразование с возможностью NULL, если имеется тождественное преобразование или неявное преобразование из A в B;
явное преобразование, допускающее значение NULL, если существует явное преобразование из A в B;
в противном случае недопустимо.
Преобразование из Nullable<A> в B:
явное преобразование в значение NULL, если имеется идентичное преобразование или неявное или явное числовое преобразование из A в B;
в противном случае недопустимо.
Преобразование из Nullable<A> в Nullable<B>:
тождественное преобразование, если существует тождественное преобразование из A в B;
явное преобразование, допускающее значение NULL, если имеется неявное или явное числовое преобразование из A в B;
в противном случае недопустимо.
Операторы
Предопределенные операторы приведены следующим образом.
Эти операторы учитываются при разрешении перегрузки по обычным правилам для неявных преобразований , если хотя бы один из операндов имеет тип nint или nuint.
(IL для каждого оператора включает варианты для контекстов unchecked и checked, если они различаются.)
Одинарный
Подпись оператора
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
Двоичный
Подпись оператора
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
Для некоторых двоичных операторов операторы IL поддерживают дополнительные типы операндов (см. таблицу типов операндов в ECMA-335 III.1.5).
Но набор типов операндов, поддерживаемых C#, ограничен для простоты и согласованности с существующими операторами на языке.
Поддерживаются версии операторов, где аргументы и типы возвращаемых значений имеют типы nint? и nuint?.
Операции комплексного присваивания x op= y, где x или y являются целыми числами, следуют тем же правилам, что и другие примитивные типы с предопределенными операторами.
В частности, выражение привязано как x = (T)(x op y), где T является типом x и где x вычисляется только один раз.
Операторы смены должны маскировать количество битов, которые необходимо переместить - на 5 битов, если sizeof(nint) равен 4, и до 6 бит, если sizeof(nint) равен 8.
(см. §12.11) в спецификации C#.
Компилятор C#9 сообщает об ошибках привязки к предопределенным собственным целым операторам при компиляции с более ранней версией языка, но позволит использовать предопределенные преобразования в собственные целые числа и из нее.
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
}
}
Арифметика указателя
В C# нет предопределенных операторов сложения или вычитания указателей с нативными целыми числами смещения.
Вместо этого значения nint и nuint продвигаются до long и ulong, а арифметика указателей использует предопределенные операторы для этих типов.
двоичные числовые продвижения информативный текст (см. §12.4.7.3) в спецификации C#, обновляется следующим образом:
…
В противном случае, если либо операнд имеет тип ulong, другой операнд преобразуется в тип ulong, или возникает ошибка времени привязки, если другой операнд имеет тип sbyte, short, int, nintили long.
В противном случае, если хотя бы один операнд имеет тип nuint, другой операнд преобразуется в тип nuint, или возникает ошибка времени привязки, если другой операнд имеет тип sbyte, short, int, nintили long.
В противном случае, если какой-либо операнд имеет тип long, другой операнд преобразуется в тип long.
В противном случае, если либо операнд имеет тип uint, а другой операнда имеет тип sbyte, short, nint, или int, оба операнда преобразуются в тип long.
В противном случае, если какой-либо операнд имеет тип uint, другой операнд преобразуется в тип uint.
В противном случае, если один операнд имеет тип nint, другой операнд преобразуется в тип nint.
В противном случае оба операнда преобразуются в тип int.
Динамический
Преобразования и операторы синтезируются компилятором и не являются частью базовых IntPtr и UIntPtr типов.
В результате эти преобразования и операторы недоступны из привязки среды выполнения для 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'
Члены типа
Единственным конструктором для nint или nuint является конструктор без параметров.
Следующие члены System.IntPtr и System.UIntPtrявным образом исключаются из nint или 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();
Оставшиеся члены System.IntPtr и System.UIntPtr, а также, неявно включены в в nint и nuint. Для .NET Framework 4.7.2:
public override bool Equals(object obj);
public override int GetHashCode();
public override string ToString();
public string ToString(string format);
Интерфейсы, реализованные System.IntPtr и System.UIntPtr, неявно включаются в nint и nuint, при этом базовые типы заменяются на соответствующие нативные целочисленные типы.
Например, если IntPtr реализует ISerializable, IEquatable<IntPtr>, IComparable<IntPtr>, nint реализует ISerializable, IEquatable<nint>, IComparable<nint>.
Переопределение, скрытие и реализация
nint и System.IntPtr, а также nuint и System.UIntPtr, считаются эквивалентными для переопределения, скрытия и реализации.
Перегрузки не могут отличаться только параметрами nint и System.IntPtr, а также nuint и System.UIntPtr.
Переопределения и реализации могут отличаться nint и System.IntPtr, а также nuint и System.UIntPtr.
Методы скрывают другие методы, отличающиеся от nint и System.IntPtr, или nuint и System.UIntPtr, только.
Разное
nint и nuint выражения, используемые в качестве индексов массива, эмитируются без преобразования.
sizeof(nint) и sizeof(nuint) поддерживаются, но требуют компиляции в небезопасном контексте (как это необходимо для sizeof(IntPtr) и sizeof(UIntPtr)).
Значения не являются константами во время компиляции.
sizeof(nint) реализуется как sizeof(IntPtr), а не IntPtr.Size; sizeof(nuint) реализуется как sizeof(UIntPtr), а не UIntPtr.Size.
Диагностика компилятора для ссылок на тип, затрагивающих nint или nuint, сообщает о nint или nuint, а не о IntPtr или UIntPtr.
Метаданные
nint и nuint представлены в метаданных как System.IntPtr и System.UIntPtr.
Ссылки на тип, включающие nint или nuint, создаются с помощью System.Runtime.CompilerServices.NativeIntegerAttribute, чтобы указать, какие части ссылки на тип являются собственными.
Кодировка ссылок на тип с помощью NativeIntegerAttribute рассматривается в NativeIntegerAttribute.md.
Альтернативы
Альтернативой упомянутому выше подходу "type erasure" является введение новых типов: System.NativeInt и System.NativeUInt.
public readonly struct NativeInt
{
public IntPtr Value;
}
Различные типы позволяют перегружать отдельно от IntPtr и обеспечивать уникальный синтаксический анализ с ToString().
Но для CLR было бы больше работы по эффективной обработке этих типов, что подрывает основную цель данной функции — повысить эффективность.
И взаимодействие с существующим нативным кодом int, использующим IntPtr, было бы сложнее.
Еще одна альтернатива — добавить более встроенную поддержку int для IntPtr в платформе, но без какой-либо определенной поддержки компилятора.
Любые новые преобразования и арифметические операции будут поддерживаться компилятором автоматически.
Но язык не будет предоставлять ключевые слова, константы или операции checked.
Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.
Отзыв о C# feature specifications
C# feature specifications — это проект с открытым исходным кодом. Выберите ссылку, чтобы оставить отзыв:
Присоединитесь к серии встреч для создания масштабируемых решений искусственного интеллекта на основе реальных вариантов использования с другими разработчиками и экспертами.