注意
本文是特性规范。 此规范是功能的设计文档。 它包括建议的规范变更,以及功能设计和开发过程中所需的信息。 这些文章将持续发布,直至建议的规范变更最终确定并纳入当前的 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 类型,称为简单类型。 简单类型通过关键字标识,但这些关键字只是 struct 命名空间中预定义 System 类型的别名,如下表所述。
| 关键字 | 别名类型 |
|---|---|
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可以转换为类型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ₓ] 表达式访问的,其中 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、uintnint 或 nuint 的表达式,表达式 long 和 ulong 计算类型 P + N 的指针值,该值是通过将 N + P 添加到 T* 给出的地址而得到的。 同样,表达式 P – N 计算出从 T*给定的地址中减去 N * sizeof(T) 后得到的 P 类型的指针值。
各种注意事项
中断性变更
此设计的主要影响之一是 System.IntPtr 和 System.UIntPtr 获得一些内置运算符(转换、一元和二进制)。
其中包括 checked 运算符,这意味着这些类型上的以下运算符在溢出时将抛出:
IntPtr + intIntPtr - intIntPtr -> intlong -> IntPtrvoid* -> IntPtr
元数据编码
这种设计意味着 nint 和 nuint 可以简单地以 System.IntPtr 和 System.UIntPtr 的形式发出,而无需使用 System.Runtime.CompilerServices.NativeIntegerAttribute。
同样,在加载元数据 NativeIntegerAttribute 时,可以忽略它。