ネイティブ サイズの整数
手記
この記事は機能仕様です。 仕様は、機能の設計ドキュメントとして機能します。 これには、提案された仕様の変更と、機能の設計と開発時に必要な情報が含まれます。 これらの記事は、提案された仕様の変更が最終決定され、現在の ECMA 仕様に組み込まれるまで公開されます。
機能の仕様と完成した実装の間には、いくつかの違いがある可能性があります。 これらの違いは、関連する 言語設計会議 (LDM) ノートでキャプチャされます。
機能仕様を C# 言語標準に導入するプロセスの詳細については、仕様に関する記事を参照してください。
概要
ネイティブ サイズの符号付き整数型と符号なし整数型の言語サポート。
動機は、相互運用シナリオと低レベルライブラリです。
設計
nint
識別子と nuint
識別子は、ネイティブ符号付き整数型と符号なし整数型を表す新しいコンテキスト キーワードです。
識別子は、名前検索でそのプログラムの場所で実行可能な結果が見つからない場合にのみキーワードとして扱われます。
nint x = 3;
_ = nint.Equals(x, 3);
nint
型と nuint
型は、基になる型 System.IntPtr
および System.UIntPtr
で表され、コンパイラによってネイティブの int としてその型に対する追加の変換や操作が行われます。
定数
定数式は、nint
型または nuint
型にすることができます。
ネイティブ int リテラルの直接構文はありません。 代わりに、他の整数定数値の暗黙的または明示的なキャストを使用できます:const nint i = (nint)42;
.
nint
定数は、[int.MinValue
, int.MaxValue
] の範囲内です。
nuint
定数は、[uint.MinValue
, uint.MaxValue
] の範囲内です。
nint
または nuint
に MinValue
または MaxValue
フィールドはありません。nuint.MinValue
以外は、これらの値を定数として出力できないためです。
定数折りたたみは、すべての単項演算子 { +
、-
、~
} および二項演算子 { +
、-
、*
、/
%
、==
、!=
、<
、<=
、>
、>=
、&
、|
、^
、<<
、>>
} でサポートされています。
定数フォールディング操作は、コンパイラ プラットフォームに関係なく一貫した動作を実現するために、ネイティブ int ではなく、Int32
および UInt32
オペランドを使用して評価されます。
演算の結果として 32 ビットの定数値が返される場合は、コンパイル時に定数フォールディングが実行されます。
それ以外の場合、操作は実行時に実行され、定数とは見なされません。
コンバージョン
nint
と IntPtr
の間、および nuint
と UIntPtr
の間には ID 変換があります。
ネイティブ int と基になる型のみが異なる複合型 (配列、Nullable<>
、構築型、およびタプル) の間では、同一性変換が行われます。
次の表では、特殊な型間の変換について説明します。
(各変換の IL には、unchecked
と checked
コンテキストのバリアントが含まれます (異なる場合)。
次の表の一般的な注意事項:
conv.u
はネイティブ整数へのゼロ拡張変換であり、conv.i
はネイティブ整数への符号拡張変換です。- 拡大と縮小の両方の
checked
コンテキストは次のとおりです。-
signed to *
のconv.ovf.*
-
unsigned to *
のconv.ovf.*.un
-
- 拡大 の
unchecked
コンテキストは次のとおりです。-
signed to *
のconv.i*
(ここで * は目標幅を指します) -
unsigned to *
のconv.u*
(ここで * は目標幅を指します)
-
- 縮小 の
unchecked
コンテキストは次のとおりです。-
any to signed *
のconv.i*
(ここで * は目標幅を指します) -
any to unsigned *
のconv.u*
(ここで * は目標幅を指します)
-
いくつかの例を取る:
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
ではchecked void* to long
がconv.ovf.i8.un
を使用するのと同じ方法でconv.ovf.i.un
を使用します。
オペランド | ターゲット | 変換 | イリノイ |
---|---|---|---|
object |
nint |
開封の儀 | unbox |
void* |
nint |
ポインタートゥボイド (PointerToVoid) | nop / conv.ovf.i.un |
sbyte |
nint |
ImplicitNumeric | conv.i |
byte |
nint |
インプリシットニューメリック | 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 |
None | |
object |
nuint |
ボックス化解除 | unbox |
void* |
nuint |
PointerToVoid | 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 |
インプリシット・ヌメリック | 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 |
None | |
UIntPtr |
nuint |
同一性 | |
列挙 | nint |
ExplicitEnumeration | |
列挙 | nuint |
ExplicitEnumeration |
オペランド | ターゲット | 変換 | イリノイ |
---|---|---|---|
nint |
object |
ボクシング | box |
nint |
void* |
PointerToVoid | nop / conv.ovf.u |
nint |
nuint |
ExplicitNumeric | conv.u (省略可能) / conv.ovf.u |
nint |
sbyte |
ExplicitNumeric | conv.i1 / conv.ovf.i1 |
nint |
byte |
エクスプリシット・ナメリック | 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 |
None | |
nint |
列挙 | ExplicitEnumeration | |
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 |
None | |
nuint |
UIntPtr |
同一性 | |
nuint |
列挙 | ExplicitEnumeration |
A
から Nullable<B>
への変換は次のとおりです。
- ID 変換または
A
からB
への暗黙的な変換がある場合は、暗黙的な null 許容変換。 A
からB
への明示的な変換がある場合は、明示的な null 許容変換。- それ以外の場合は無効です。
Nullable<A>
から B
への変換は次のとおりです。
- ID 変換、または
A
からB
への暗黙的または明示的な数値変換がある場合は、明示的な null 許容変換。 - それ以外の場合は無効です。
Nullable<A>
から Nullable<B>
への変換は次のとおりです。
A
からB
への ID 変換がある場合の ID 変換。A
からB
への暗黙的または明示的な数値変換がある場合は、明示的な null 許容変換。- それ以外の場合は無効です。
Operators
定義済みの演算子は次のとおりです。
これらの演算子は、オーバーロードの解決中に、少なくとも 1 つのオペランドが
(各演算子の IL には、unchecked
と checked
コンテキストのバリアントが含まれます (異なる場合)。
単項演算子 | 演算子の署名 | イリノイ |
---|---|---|
+ |
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 |
バイナリ | 演算子の署名 | イリノイ |
---|---|---|
+ |
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
がネイティブ int である場合は、定義済みの演算子を持つ他のプリミティブ型と同じ規則に従います。
具体的には、式は、T
が x
の型であり、x
が 1 回だけ評価される x = (T)(x op y)
としてバインドされます。
シフト演算子は、シフトするビット数をマスクする必要があります。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# に定義済みの演算子はありません。
代わりに、nint
値と nuint
値は long
に昇格され、ulong
およびポインターの算術演算では、これらの型に対して定義済みの演算子が使用されます。
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
に変換されます。または、もう一方のオペランドが型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
式は、変換なしで出力されます。
static object GetItem(object[] array, nint index)
{
return array[index]; // ok
}
nint
と nuint
は、C# の enum
基本型として使用できません。
enum E : nint // error: byte, sbyte, short, ushort, int, uint, long, or ulong expected
{
}
読み取りと書き込みは、nint
と nuint
に対してアトミックです。
フィールドは、nint
型と nuint
型の volatile
としてマークできます。
ただし、ECMA-334 15.5.4 には、基本タイプ System.IntPtr
または System.UIntPtr
を持つ enum
は含まれません。
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)
は、IntPtr.Size
ではなく sizeof(IntPtr)
として実装されます。sizeof(nuint)
は、UIntPtr.Size
ではなく sizeof(UIntPtr)
として実装されます。
nint
または nuint
に関わる型参照についてコンパイラ診断は、IntPtr
や UIntPtr
ではなく、nint
または nuint
を報告します。
メタデータ
nint
と nuint
は、メタデータで System.IntPtr
および System.UIntPtr
として表されます。
nint
または nuint
を含む型参照は、型参照のどの部分がネイティブ int であるかを示す System.Runtime.CompilerServices.NativeIntegerAttribute
と共に出力されます。
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.NativeInt
と System.NativeUInt
) を導入します。
public readonly struct NativeInt
{
public IntPtr Value;
}
異なる型を使用すると、IntPtr
とは異なるオーバーロードが可能になり、個別の解析と ToString()
が可能になります。
ただし、CLR でこれらの型を効率的に処理する作業が増え、機能の主な目的である効率が低下します。
また、IntPtr
を使用する既存のネイティブ int コードとの相互運用は、より困難になります。
もう 1 つの方法は、フレームワーク内の IntPtr
に対するネイティブな int サポートを追加することですが、特定のコンパイラのサポートはありません。
新しい変換と算術演算は、コンパイラによって自動的にサポートされます。
ただし、言語はキーワード、定数、または checked
操作を提供しません。
デザインに関する会議
- https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-05-26.md
- https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-06-13.md
- https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-07-05.md#native-int-and-intptr-operators
- https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-10-23.md
- https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-03-25.md
C# feature specifications