Bagikan melalui


Bilangan bulat berukuran asli

Nota

Artikel ini adalah spesifikasi fitur. Spesifikasi berfungsi sebagai dokumen desain untuk fitur tersebut. Ini termasuk perubahan spesifikasi yang diusulkan, bersama dengan informasi yang diperlukan selama desain dan pengembangan fitur. Artikel ini diterbitkan sampai perubahan spesifikasi yang diusulkan diselesaikan dan dimasukkan dalam spesifikasi ECMA saat ini.

Mungkin ada beberapa perbedaan antara spesifikasi fitur dan implementasi yang selesai. Perbedaan tersebut dicatat dalam catatan terkait rapat desain bahasa (LDM) .

Anda dapat mempelajari lebih lanjut tentang proses untuk mengadopsi speklet fitur ke dalam standar bahasa C# dalam artikel tentang spesifikasi .

Masalah juara: https://github.com/dotnet/csharplang/issues/435

Ringkasan

Dukungan bahasa untuk jenis bilangan bulat berukuran asli yang ditandatangani dan tidak ditandatangani.

Motivasinya adalah untuk skenario interoperabilitas dan untuk perpustakaan tingkat rendah.

Desain

Pengidentifikasi nint dan nuint adalah kata kunci kontekstual baru yang mewakili jenis bilangan bulat asli yang ditandatangani dan tidak ditandatangani. Pengidentifikasi hanya diperlakukan sebagai kata kunci ketika pencarian nama tidak menemukan hasil yang layak di lokasi program tersebut.

nint x = 3;
_ = nint.Equals(x, 3);

Jenis nint dan nuint diwakili oleh jenis dasar System.IntPtr dan System.UIntPtr, dimana compiler menampilkan konversi dan operasi tambahan untuk jenis tersebut sebagai bilangan bulat asli.

Konstanta

Ekspresi konstanta mungkin berjenis nint atau nuint. Tidak ada sintaks langsung untuk literal integer bawaan. Cast implisit atau eksplisit dari nilai konstanta integral lainnya dapat digunakan sebagai alternatif: const nint i = (nint)42;.

nint konstanta berada dalam rentang [ int.MinValue, int.MaxValue ].

nuint konstanta berada dalam rentang [ uint.MinValue, uint.MaxValue ].

Tidak ada bidang MinValue atau MaxValue pada nint atau nuint karena, selain nuint.MinValue, nilai-nilai tersebut tidak dapat dipancarkan sebagai konstanta.

Lipatan konstan didukung untuk semua operator unary { +, -, ~ } dan operator biner { +, -, *, /, %, ==, !=, <, <=, >, >=, &, |, ^, <<, >> }. Operasi pelipatan konstan dievaluasi dengan operand Int32 dan UInt32 alih-alih integer asli agar memberikan perilaku yang konsisten terlepas dari platform pengkompilasi. Jika operasi menghasilkan nilai konstanta dalam 32-bit, pelipatan konstan dilakukan pada waktu kompilasi. Jika tidak, operasi dijalankan pada runtime dan tidak dianggap sebagai konstanta.

Konversi

Ada konversi identitas antara nint dan IntPtr, dan antara nuint dan UIntPtr. Ada konversi identitas antara jenis gabungan yang berbeda hanya dengan inti asli dan jenis yang mendasari: array, Nullable<>, jenis terbangun, dan tuples.

Tabel di bawah ini mencakup konversi antara tipe khusus. (IL untuk setiap konversi mencakup varian untuk konteks unchecked dan checked jika berbeda.)

Catatan umum pada tabel di bawah ini:

  • conv.u adalah perpanjangan nol ke integer asli dan conv.i adalah perpanjangan tanda ke integer asli.
  • konteks checked untuk pelebaran dan penyempitan adalah:
    • conv.ovf.* untuk signed to *
    • conv.ovf.*.un untuk unsigned to *
  • konteks unchecked untuk pelebaran adalah:
    • conv.i* untuk signed to * (di mana * adalah lebar target)
    • conv.u* untuk unsigned to * (di mana * adalah lebar target)
  • unchecked konteks untuk penyempitan adalah:
    • conv.i* untuk any to signed * (di mana * adalah lebar target)
    • conv.u* untuk any to unsigned * (di mana * adalah lebar target)

Mengambil beberapa contoh:

  • sbyte to nint dan sbyte to nuint menggunakan conv.i saat byte to nint dan byte to nuint menggunakan conv.u karena semuanya melebar.
  • nint to byte dan nuint to byte menggunakan conv.u1 saat nint to sbyte dan nuint to sbyte menggunakan conv.i1. Untuk byte, sbyte, short, dan ushort, "jenis tumpukan" adalah int32. Jadi, conv.i1 secara efektif "dikonversi turun ke byte bertanda dan kemudian diperluas tandanya hingga int32" sementara conv.u1 secara efektif "dikonversi turun ke byte tak bertanda dan kemudian diperluas dengan nol hingga int32".
  • checked void* to nint menggunakan conv.ovf.i.un cara yang sama seperti checked void* to long menggunakan conv.ovf.i8.un.
Operand Target Konversi IL
object nint Unboxing unbox
void* nint PointerToVoid (Pointer ke Void) nop / conv.ovf.i.un
sbyte nint ImplisitNumerik conv.i
byte nint ImplisitNumerik conv.u
short nint ImplisitNumerik conv.i
ushort nint ImplisitNumerik conv.u
int nint ImplisitNumerik 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 ImplisitNumerik 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 Identitas
UIntPtr nint Tidak
object nuint Unboxing unbox
void* nuint PointerToVoid (Pointer ke Void) nop
sbyte nuint ExplicitNumeric conv.i / conv.ovf.u
byte nuint ImplisitNumerik conv.u
short nuint ExplicitNumeric conv.i / conv.ovf.u
ushort nuint ImplisitNumerik conv.u
int nuint ExplicitNumeric conv.i / conv.ovf.u
uint nuint ImplisitNumerik conv.u
long nuint ExplicitNumeric conv.u / conv.ovf.u
ulong nuint ExplicitNumeric conv.u / conv.ovf.u.un
char nuint ImplisitNumerik 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 Tidak
UIntPtr nuint Identitas
Enumerasi nint EnumerasiEksplisit (ExplicitEnumeration)
Enumerasi nuint EnumerasiEksplisit (ExplicitEnumeration)
Operand Target Konversi IL
nint object Tinju box
nint void* PointerToVoid (Pointer ke Void) nop / conv.ovf.u
nint nuint ExplicitNumeric conv.u (dapat dihilangkan) / 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 ImplisitNumerik conv.i8
nint ulong ExplicitNumeric conv.i8 / conv.ovf.u8
nint char ExplicitNumeric conv.u2 / conv.ovf.u2
nint float ImplisitNumerik conv.r4
nint double ImplisitNumerik conv.r8
nint decimal ImplisitNumerik conv.i8 decimal decimal.op_Implicit(long)
nint IntPtr Identitas
nint UIntPtr Tidak
nint Enumerasi EnumerasiEksplisit (ExplicitEnumeration)
nuint object Tinju box
nuint void* PointerToVoid (Pointer ke Void) nop
nuint nint ExplicitNumeric conv.i(dapat dihilangkan) / 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 ImplisitNumerik conv.u8
nuint char ExplicitNumeric conv.u2 / conv.ovf.u2.un
nuint float ImplisitNumerik conv.r.un conv.r4
nuint double ImplisitNumerik conv.r.un conv.r8
nuint decimal ImplisitNumerik conv.u8 decimal decimal.op_Implicit(ulong)
nuint IntPtr Tidak
nuint UIntPtr Identitas
nuint Enumerasi EnumerasiEksplisit (ExplicitEnumeration)

Konversi dari A ke Nullable<B> adalah:

  • konversi implisit nullable jika ada konversi identitas atau konversi implisit dari A ke B;
  • konversi bertipe nullable eksplisit jika terdapat konversi eksplisit dari A ke B;
  • jika tidak, maka tidak valid

Konversi dari Nullable<A> ke B adalah:

  • konversi eksplisit nullable jika ada konversi identitas atau konversi numerik implisit atau eksplisit dari A ke B;
  • jika tidak, maka tidak valid

Konversi dari Nullable<A> ke Nullable<B> adalah:

  • konversi identitas jika ada konversi identitas dari A ke B;
  • konversi nullable eksplisit jika ada konversi numerik implisit atau eksplisit dari A ke B;
  • jika tidak, maka tidak valid

Operator

Operator yang telah ditentukan sebelumnya adalah sebagai berikut. Operator ini dipertimbangkan selama resolusi kelebihan beban berdasarkan aturan normal untuk konversi implisit jika setidaknya salah satu operand berjenis nint atau nuint.

(IL untuk setiap operator mencakup varian untuk konteks unchecked dan checked jika berbeda.)

Unari Tanda Tangan Operator 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
Biner Tanda Tangan Operator 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

Untuk beberapa operator biner, operator IL mendukung jenis operand tambahan (lihat tabel jenis ECMA-335 III.1.5 Operand). Tetapi set jenis operand yang didukung oleh C# terbatas demi kesederhanaan dan untuk konsistensi dengan operator yang ada dalam bahasa ini.

Versi operator yang telah diangkat, dengan argumen dan jenis pengembalian berupa nint? dan nuint?, didukung.

Operasi penugasan gabungan x op= y di mana x atau y adalah int bawaan mengikuti aturan yang sama seperti jenis primitif lainnya dengan operator yang sudah ditetapkan. Secara khusus ekspresi terikat sebagai x = (T)(x op y) di mana T adalah jenis x dan di mana x hanya dievaluasi sekali.

Operator shift harus menutupi jumlah bit yang akan digeser - ke 5 bit jika sizeof(nint) adalah 4, dan menjadi 6 bit jika sizeof(nint) adalah 8. (lihat §12.11) dalam spesifikasi C#).

Pengkompilasi C#9 akan melaporkan kesalahan yang mengikat ke operator bilangan bulat asli yang telah ditentukan saat mengkompilasi dengan versi bahasa yang lebih lama, tetapi akan memungkinkan penggunaan konversi yang telah ditentukan sebelumnya ke dan dari bilangan bulat asli.

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

Aritmatika penunjuk

Tidak ada operator yang telah ditentukan sebelumnya di C# untuk penambahan atau pengurangan pointer dengan offset bilangan bulat asli. Sebagai gantinya, nilai nint dan nuint dipromosikan ke long dan ulong dan aritmatika pointer menggunakan operator yang telah ditentukan sebelumnya untuk jenis tersebut.

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)

Peningkatan numerik biner

promosi numerik biner teks informatif (lihat §12.4.7.3) dalam spesifikasi C# telah diperbarui sebagai berikut:

  • Jika tidak, jika salah satu operand berjenis ulong, operand lain dikonversi ke jenis ulong, atau kesalahan waktu pengikatan terjadi jika operand lain berjenis sbyte, short, int, nint, atau long.
  • Jika tidak, jika salah satu operand berjenis nuint, operand lain dikonversi ke jenis nuint, atau kesalahan waktu pengikatan terjadi jika operand lain berjenis sbyte, short, int, nint, atau long.
  • Jika tidak, jika salah satu operan berjenis long, operand lainnya dikonversi ke jenis long.
  • Jika tidak, jika salah satu operan berjenis uint dan operand lainnya berjenis sbyte, short, nint, atau int, kedua operan dikonversi ke jenis long.
  • Jika tidak, jika salah satu operan berjenis uint, operand lainnya dikonversi ke jenis uint.
  • Jika tidak, jika salah satu operan berjenis nint, operand lainnya dikonversi ke jenis nint.
  • Jika tidak, kedua operan dikonversi ke jenis int.

Dinamis

Konversi dan operator disintesis oleh pengkompilasi dan bukan bagian dari jenis IntPtr dan UIntPtr yang mendasar. Akibatnya, konversi dan operator tersebut tidak tersedia dari pengikat waktu proses untuk 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'

Jenis anggota

Satu-satunya konstruktor untuk nint atau nuint adalah konstruktor tanpa parameter.

Anggota System.IntPtr dan System.UIntPtrberikut secara eksplisit dikecualikan dari nint atau 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();

Anggota System.IntPtr dan System.UIntPtryang tersisa secara implisit disertakan dalam dalam nint dan nuint. Untuk .NET Framework 4.7.2:

public override bool Equals(object obj);
public override int GetHashCode();
public override string ToString();
public string ToString(string format);

Antarmuka yang diimplementasikan oleh System.IntPtr dan System.UIntPtrsecara implisit disertakan dalam nint dan nuint, dengan kemunculan tipe dasar digantikan oleh tipe bilangan bulat asli yang sesuai. Misalnya jika IntPtr menerapkan ISerializable, IEquatable<IntPtr>, IComparable<IntPtr>, maka nint menerapkan ISerializable, IEquatable<nint>, IComparable<nint>.

Mengambil alih, menyembunyikan, dan mengimplementasikan

nint dan System.IntPtr, dan nuint dan System.UIntPtr, dianggap setara untuk mengambil alih, menyembunyikan, dan menerapkan.

Kelebihan beban tidak dapat berbeda dengan nint dan System.IntPtr, dan nuint dan System.UIntPtr, saja. Penimpaan dan implementasi dapat berbeda hanya dengan nint dan System.IntPtr, atau nuint dan System.UIntPtr. Metode menyembunyikan metode lain yang hanya berbeda pada nint dan System.IntPtr, atau nuint dan System.UIntPtr.

Lain-lain

ekspresi nint dan nuint yang digunakan sebagai indeks array dipancarkan tanpa konversi.

static object GetItem(object[] array, nint index)
{
    return array[index]; // ok
}

nint dan nuint tidak dapat digunakan sebagai jenis dasar enum dari C#.

enum E : nint // error: byte, sbyte, short, ushort, int, uint, long, or ulong expected
{
}

Pembacaan dan penulisan bersifat atomik untuk nint dan nuint.

Bidang dapat ditandai volatile untuk jenis nint dan nuint. ECMA-334 15.5.4 tidak menyertakan enum dengan jenis dasar System.IntPtr atau System.UIntPtr.

default(nint) dan new nint() setara dengan (nint)0; default(nuint) dan new nuint() setara dengan (nuint)0.

typeof(nint) adalah typeof(IntPtr); typeof(nuint) adalah typeof(UIntPtr).

sizeof(nint) dan sizeof(nuint) didukung tetapi memerlukan kompilasi dalam konteks yang tidak aman (sebagaimana diperlukan untuk sizeof(IntPtr) dan sizeof(UIntPtr)). Nilai bukan konstanta waktu kompilasi. sizeof(nint) diimplementasikan sebagai sizeof(IntPtr) daripada IntPtr.Size; sizeof(nuint) diimplementasikan sebagai sizeof(UIntPtr) daripada UIntPtr.Size.

Diagnostik pengkompilasi untuk referensi tipe yang melibatkan nint atau nuint melaporkan nint atau nuint daripada IntPtr atau UIntPtr.

Metadata

nint dan nuint diwakili dalam metadata sebagai System.IntPtr dan System.UIntPtr.

Referensi tipe yang mencakup nint atau nuint dikeluarkan dengan System.Runtime.CompilerServices.NativeIntegerAttribute untuk menunjukkan bagian mana dari referensi tipe yang merupakan integer asli.

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;
    }
}

Pengodean referensi jenis dengan NativeIntegerAttribute tercakup dalam NativeIntegerAttribute.md.

Alternatif

Alternatif untuk pendekatan "jenis penghapusan" di atas adalah memperkenalkan jenis baru: System.NativeInt dan System.NativeUInt.

public readonly struct NativeInt
{
    public IntPtr Value;
}

Tipe berbeda akan memungkinkan kelebihan beban yang berbeda dari IntPtr dan memungkinkan penguraian yang berbeda serta ToString(). Tetapi akan ada lebih banyak pekerjaan bagi CLR untuk menangani jenis ini secara efisien yang mengalahkan tujuan utama fitur - efisiensi. Dan interoperabilitas dengan kode int asli yang ada yang menggunakan IntPtr akan lebih sulit.

Alternatif lain adalah menambahkan lebih banyak dukungan int asli untuk IntPtr dalam kerangka kerja tetapi tanpa dukungan kompilator tertentu. Setiap konversi baru dan operasi aritmatika akan didukung oleh pengkompilasi secara otomatis. Tetapi bahasa tidak akan menyediakan kata kunci, konstanta, atau operasi checked.

Rapat desain