数值 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
类型,称为简单类型。 简单类型通过关键字标识,但这些关键字只是 System
命名空间中预定义 struct
类型的别名,如下表所述。
关键字 | 别名类型 |
---|---|
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# 支持 11 个整型类型:sbyte
、byte
、short
、ushort
、int
、uint
、nint
、nuint
、、long
、ulong
和 char
。 [...]
8.8 非托管类型
换言之,unmanaged_type 即为以下类型之一:
-
sbyte
、byte
、short
、ushort
、int
、uint
、nint
、nuint
、long
、ulong
、char
、float
、double
、decimal
或bool
。 - 任何 enum_type。
- 任何不是构造类型且仅包含非 unmanaged_type 字段的用户定义的 struct_type。
- 在不安全的代码中,任何 pointer_type。
10.2.3 隐式数值转换
隐式数值转换包括:
- 从
sbyte
到short
、int
、nint
、long
、float
、double
或decimal
。 - 从
byte
到short
、ushort
、int
、uint
、nint
、nuint
、long
、ulong
、float
、double
或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 隐式常量表达式转换
隐式常量表达式转换允许以下转换:
int
类型的 constant_expression 可以转换为类型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
、nuint
、long
、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
是 array_type 的 primary_no_array_creation_expression,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 一元加运算符
预定义的一元加运算符为:
...
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 按位求补运算符
预定义的按位补运算符有以下几种:
...
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
为负位,则设置为 1。当
x
的类型为uint
、nuint
或ulong
时,x
的低阶位会被丢弃,其余位被右移,高阶空位位置被置零。无符号右移:
... nint operator >>>(nint x, int count); nuint operator >>>(nuint x, int count);
对于预定义的运算符,要移动的位数计算如下:[...]
- 当
x
的类型为nint
或nuint
时,移位计数由 32 位平台上的count
的低阶 5 位或 64 位平台上的count
的低阶 6 位给出。
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 数组元素访问
数组元素是使用表单 A[I₁, I₂, ..., Iₓ]
的 element_access 表达式访问的,其中 A
是数组类型的表达式,每个 Iₑ
是类型 int
、uint
、nint
、nuint
、long
、ulong
或可隐式转换为其中一个或多个类型的表达式。 数组元素访问的结果是一个变量,即索引选择的数组元素。
23.5 指针转换
23.5.1 概述
[...]
此外,在不安全的上下文中,可用的显式转换集被扩展为包括以下显式指针转换:
- 从任何 pointer_type 到任何其他 pointer_type。
- 从
sbyte
、byte
、short
、ushort
、int
、uint
、nint
、nuint
、long
或ulong
到任何 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
、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
时,可以忽略它。