共用方式為


原生大小的整數

注意

本文是功能規格。 規格可作為功能的設計檔。 其中包含建議的規格變更,以及功能設計和開發期間所需的資訊。 這些文章會發佈,直到提議的規格變更完成並併併入目前的ECMA規格為止。

功能規格與已完成實作之間可能有一些差異。 這些差異是在的相關 語言設計會議(LDM)注意事項中擷取的。

您可以在 規格一文中,深入瞭解將功能規格納入 C# 語言標準的流程

總結

原生帶正負號和不帶正負號整數類型的語言支援。

動機適用於互操作性案例及低階函式庫。

設計

標識碼 nintnuint 是代表原生帶正負號和不帶正負號整數類型的新內容關鍵詞。 只有在名稱查閱在該程式位置找不到可行的結果時,才會將標識碼視為關鍵詞。

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

類型 nintnuint 是由基礎類型 System.IntPtrSystem.UIntPtr 來表示,編譯器會將這些類型的額外轉換和操作視為原生整數。

常數

常數表示式的類型可以是 nintnuint。 原生 int 字面值沒有直接的語法。 您可以改用其他整數常數值的隱含或明確轉換:const nint i = (nint)42;

nint 常數位於範圍 [ int.MinValueint.MaxValue ]。

nuint 常數位於範圍 [ uint.MinValueuint.MaxValue ]。

nintnuint 上沒有 MinValueMaxValue 字段,因為除了 nuint.MinValue以外,這些值無法發出為常數。

所有一元運算符 { +-~ } 和二元運算符 { +-*/%==!=<<=>>=&|^<<>> } 都支援常量折疊。 不論編譯器平台為何,都會使用 Int32UInt32 操作數來進行常數折疊運算,以確保一致的行為,而不是使用原生 int。 如果作業產生 32 位的常數值,則會在編譯階段執行常數折疊。 否則,作業會在運行時間執行,而不會被視為常數。

轉換

nintIntPtr之間,以及 nuintUIntPtr之間有同一性轉換。 只有在原生 int 和基礎類型不同的情況下,複合類型之間存在識別轉換:陣列、Nullable<>、構建類型和 Tuple。

下表涵蓋特殊類型之間的轉換。 (每個轉換的 IL 都包含 uncheckedchecked 上下文的變體,如果這些上下文有所不同。)

下表的一般注意事項:

  • conv.u 是轉換到原生整數的零擴展,conv.i 是轉換到原生整數的符號擴展。
  • 擴大縮小checked 情境如下:
    • signed to * 使用的 conv.ovf.*
    • 用於 unsigned to *conv.ovf.*.un
  • 擴大unchecked 內容如下:
    • conv.i* 替換 signed to *(其中 * 為目標寬度)
    • conv.u*unsigned to *(其中 * 是目標寬度)
  • unchecked 縮小 的情境如下:
    • conv.i*any to signed *(其中 * 表示目標寬度)
    • conv.u* 用於 any to unsigned * (其中 * 是目標寬度)

以下是一些例子:

  • sbyte to nintsbyte to nuint 使用 conv.i,而 byte to nintbyte to nuint 使用 conv.u,因為它們全都 擴大
  • nint to bytenuint to byte 使用 conv.u1nint to sbytenuint to sbyte 則使用 conv.i1。 對於 bytesbyteshortushort,其「堆疊類型」是 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 空指標 nop / conv.ovf.i.un
sbyte nint ImplicitNumeric conv.i
byte nint ImplicitNumeric conv.u
short nint ImplicitNumeric conv.i
ushort nint ImplicitNumeric conv.u
int nint ImplicitNumeric 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 ImplicitNumeric 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 身份
UIntPtr nint 沒有
object nuint 拆箱 unbox
void* nuint 指向空的指標 nop
sbyte nuint ExplicitNumeric conv.i / conv.ovf.u
byte nuint ImplicitNumeric conv.u
short nuint ExplicitNumeric conv.i / conv.ovf.u
ushort nuint ImplicitNumeric conv.u
int nuint ExplicitNumeric conv.i / conv.ovf.u
uint nuint ImplicitNumeric conv.u
long nuint ExplicitNumeric conv.u / conv.ovf.u
ulong nuint ExplicitNumeric conv.u / conv.ovf.u.un
char nuint ImplicitNumeric 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 沒有
UIntPtr nuint 身份
列舉 nint 明確列舉
列舉 nuint 顯式列舉
操作數 目標 轉換 IL
nint object 拳擊 box
nint void* 指向空值的指標 nop / conv.ovf.u
nint nuint ExplicitNumeric conv.u (可以省略) / 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 ImplicitNumeric conv.i8
nint ulong ExplicitNumeric conv.i8 / conv.ovf.u8
nint char ExplicitNumeric conv.u2 / conv.ovf.u2
nint float ImplicitNumeric conv.r4
nint double ImplicitNumeric conv.r8
nint decimal ImplicitNumeric conv.i8 decimal decimal.op_Implicit(long)
nint IntPtr 身份
nint UIntPtr 沒有
nint 列舉 顯式列舉
nuint object 拳擊 box
nuint void* PointerToVoid nop
nuint nint ExplicitNumeric conv.i(可以省略) / 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 ImplicitNumeric conv.u8
nuint char ExplicitNumeric conv.u2 / conv.ovf.u2.un
nuint float ImplicitNumeric conv.r.un conv.r4
nuint double ImplicitNumeric conv.r.un conv.r8
nuint decimal ImplicitNumeric conv.u8 decimal decimal.op_Implicit(ulong)
nuint IntPtr 沒有
nuint UIntPtr 身份
nuint 列舉 明確列舉 (ExplicitEnumeration)

A 轉換成 Nullable<B> 為:

  • 如果有從 AB的識別轉換或隱含轉換,則為隱含可為 Null 的轉換;
  • 如果存在從 AB的顯式轉換,那麼這是顯式的可為 Null 轉換。
  • 否則為無效。

Nullable<A> 轉換成 B 為:

  • 在從 AB存在識別轉換或隱含或明確的數值轉換時,即為明確的可為 Null 的轉換。
  • 否則為無效。

Nullable<A> 轉換成 Nullable<B> 為:

  • 如果有從 A 轉換成 B的身分識別轉換,則為身分識別轉換;
  • 如果存在從 AB的隱含或明確數值轉換,那麼將進行明確的可為 Null 的轉換。
  • 否則為無效。

運營商

預先定義的運算符如下所示。 這些運算子在多載解析期間會根據隱式轉換的一般規則進行考慮 ,前提是至少有一個運算元的類型為 nintnuint

每個運算子的 IL 包含不同的情況下,uncheckedchecked 內容的變體。

單一運算 操作員簽章 伊利諾伊
+ 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 其中 xy 是原生 int,會遵循與其他具有預先定義運算符的基本類型相同的規則。 具體來說,表達式會系結為 x = (T)(x op y),其中 Tx 的類型,其中 x 只會評估一次。

shift 運算子應該遮罩要移位的位數-如果 sizeof(nint) 為 4,則為 5 位,如果 sizeof(nint) 為 8,則為 6 位。 (請參閱 C# 規格中的 •12.11)。

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# 中沒有任何預先定義的運算子可用於指標與原生整數偏移量的加法或減法運算。 相反地,nintnuint 值會升階為 longulong 和指標算術會針對這些類型使用預先定義的運算符。

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)

二進位數值升階

二進位數值升階 資訊文字(請參閱 C# 規格中的 §12.4.7.3),更新如下:

  • 否則,如果任一操作數的類型為 ulong,則另一個操作數會轉換成類型 ulong,或者如果另一個操作數的類型為 sbyteshortintnintlong,則會發生系結時間錯誤。
  • 否則,如果任一操作數的類型為 nuint,則另一個操作數會轉換成類型 nuint,或者如果其他操作數的類型為 sbyteshortintnintlong,則會發生系結時間錯誤。
  • 否則,如果任一操作數的類型為 long,則另一個操作數會轉換成類型 long
  • 否則,如果任一操作數的類型為 uint,而另一個操作數的類型為 sbyteshortnintint,則兩個操作數都會轉換成類型 long
  • 否則,如果任一操作數的類型為 uint,則另一個操作數會轉換成類型 uint
  • 否則,如果任一操作數的類型為 nint,則另一個操作數會轉換成類型 nint
  • 否則,這兩個操作數都會轉換成類型 int

動態

轉換和運算符是由編譯程式所合成,不屬於基礎 IntPtrUIntPtr 型別的一部分。 因此,這些轉換和運算子 對於 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'

類型成員

nintnuint 的唯一建構函式是無參數建構函式。

下列 System.IntPtrSystem.UIntPtr成員會從 nintnuint中明確排除

// 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.IntPtrSystem.UIntPtr的其餘成員被隱含在 nintnuint中包含於。 針對 .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.IntPtrSystem.UIntPtr所實作的介面隱含地包含在 nintnuint,其基礎型別的出現會被相應的原生整數型別取代。 例如,如果 IntPtr 實作 ISerializable, IEquatable<IntPtr>, IComparable<IntPtr>,則 nint 實作 ISerializable, IEquatable<nint>, IComparable<nint>

覆寫、隱藏和實現

nintSystem.IntPtrnuintSystem.UIntPtr,被視為等同於覆寫、隱藏和實作。

多載不能僅藉由 nintSystem.IntPtr,以及 nuintSystem.UIntPtr而有所不同。 覆寫和實作可能會因 nintSystem.IntPtr而有所不同,或單獨 nuintSystem.UIntPtr。 方法會隱藏僅因 nintSystem.IntPtrnuintSystem.UIntPtr而不同的其他方法。

雜項

nintnuint 用來做為陣列索引的表達式會在不轉換的情況下發出。

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

nintnuint 無法作為 C# 的 enum 基底類型。

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

讀取和寫入在 nintnuint上是原子的。

欄位可能會標示為 volatile,用於 nintnuint類型。 ECMA-334 15.5.4 不包含 enum,其基底類型為 System.IntPtrSystem.UIntPtr

default(nint)new nint() 相當於 (nint)0;default(nuint)new nuint() 相當於 (nuint)0

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

支援 sizeof(nint)sizeof(nuint),但需要在不安全的環境中編譯(如同對 sizeof(IntPtr)sizeof(UIntPtr)的需求一樣)。 這些值不是編譯時間常數。 sizeof(nint) 會實作為 sizeof(IntPtr),而不是 IntPtr.Size;sizeof(nuint) 會實作為 sizeof(UIntPtr),而不是 UIntPtr.Size

涉及 nintnuint 型別參考的編譯器診斷報告 nintnuint,而不是 IntPtrUIntPtr

元數據

nintnuint 會以元資料表示為 System.IntPtrSystem.UIntPtr

包含 nintnuint 的類型參考會發出 System.Runtime.CompilerServices.NativeIntegerAttribute,以指出類型參考的哪些部分是原生 ints。

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

使用 NativeIntegerAttribute 的型別參考編碼涵蓋在 NativeIntegerAttribute.md中。

替代選擇

上述「類型清除」方法的替代方法是引進新的類型:System.NativeIntSystem.NativeUInt

public readonly struct NativeInt
{
    public IntPtr Value;
}

獨特的類型允許與 IntPtr 不同的重載,並允許獨特的解析和 ToString()。 不過,CLR 必須處理更多工作來有效地處理這些類型,這會使此功能失去其主要目的——效率。 使用 IntPtr 的現有原生 int 程式代碼互操作會比較困難。

另一個替代方法是在架構中新增更多針對 IntPtr 的原生整數支援,但不需要任何特定的編譯器支援。 編譯程式會自動支援任何新的轉換和算術運算。 但是語言不會提供關鍵詞、常數或 checked 作業。

設計會議