数值 IntPtr

注意

本文是特性规范。 此规范是功能的设计文档。 它包括建议的规范变更,以及功能设计和开发过程中所需的信息。 这些文章将持续发布,直至建议的规范变更最终确定并纳入当前的 ECMA 规范。

功能规范与已完成的实现之间可能存在一些差异。 这些差异记录在相关的语言设计会议 (LDM) 说明中。

可以在有关规范的文章中了解更多有关将功能规范子块纳入 C# 语言标准的过程。

支持者问题:https://github.com/dotnet/csharplang/issues/6065

总结

这是对初始本机整数特征(规范)的修订,其中 nint/nuint 类型不同于基础类型 System.IntPtr/System.UIntPtr。 简而言之,我们现在将 nint/nuint 视为简单类型,将 System.IntPtr/System.UIntPtr 混叠,就像我们对 intSystem.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 个整型类型:sbytebyteshortushortintuintnintnuintlongulongchar。 [...]

8.8 非托管类型

换言之,unmanaged_type 即为以下类型之一:

  • sbytebyteshortushortintuintnintnuintlongulongcharfloatdoubledecimalbool
  • 任何 enum_type
  • 任何不是构造类型且仅包含非 unmanaged_type 字段的用户定义的 struct_type
  • 在不安全的代码中,任何 pointer_type

10.2.3 隐式数值转换

隐式数值转换包括:

  • sbyteshortintnintlongfloatdoubledecimal
  • byteshortushortintuintnintnuintlongulongfloatdoubledecimal
  • shortintnintlongfloatdoubledecimal
  • ushortintuintnintnuintlongulongfloatdoubledecimal
  • intnintlongfloatdoubledecimal
  • uintnuintlongulongfloatdoubledecimal
  • nintlongfloatdoubledecimal
  • nuintulongfloatdoubledecimal
  • longfloatdoubledecimal
  • ulongfloatdoubledecimal
  • charushortintuintnintnuintlongulongfloatdoubledecimal
  • floatdouble

[...]

10.2.11 隐式常量表达式转换

隐式常量表达式转换允许以下转换:

  • int 类型的 constant_expression 可以转换为类型 sbytebyteshortushortuintnintnuintulong,前提是 constant_expression 的值处于目标类型的范围内。 [...]

10.3.2 显式数值转换

显式数值转换是指从一个 numeric_type 到另一个 numeric_type 的转换,而隐式数值转换尚不存在:

  • sbytebyteushortuintnuintulongchar
  • bytesbytechar
  • shortsbytebyteushortuintnuintulongchar
  • ushortsbytebyteshortchar
  • intsbytebyteshortushortuintnuintulongchar
  • uintsbytebyteshortushortintnintchar
  • longsbytebyteshortushortintuintnintnuintulongchar
  • nintsbytebyteshortushortintuintnuintulongchar
  • nuintsbytebyteshortushortintuintnintlongchar
  • ulongsbytebyteshortushortintuintnintnuintlongchar
  • charsbytebyteshort
  • floatsbytebyteshortushortintuintnintnuintlongulongchardecimal
  • doublesbytebyteshortushortintuintnintnuintlongulongcharfloatdecimal
  • decimalsbytebyteshortushortintuintnintnuintlongulongcharfloatdouble

[...]

10.3.3 显式枚举转换

显式枚举转换如下:

  • sbytebyteshortushortintuintnintnuintlongulongcharfloatdoubledecimal 到任何 enum_type
  • 从任何 enum_typesbytebyteshortushortintuintnintnuintlongulongcharfloatdoubledecimal
  • 从任何 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 的排名相同,每个表达式的类型应为 intuintnintnuintlongulong,,或者应隐式转换为这些类型中的一种或多种。

11.8.12.2 数组访问

[...] argument_list 中的表达式数量应与 array_type 的排名相同,每个表达式的类型应为 intuintnintnuintlongulong,,或者应隐式转换为这些类型中的一种或多种。

[...] 表单 P[A] 的数组访问的运行时处理包括以下步骤:其中 Parray_typeprimary_no_array_creation_expressionAargument_list:[...]

  • argument_list 的索引表达式按从左到右的顺序计算。 对每个索引表达式进行计算后,执行到以下类型之一的隐式转换:intuintnintnuintlongulong。 在此列表中,选择存在隐式转换的第一个类型。 [...]

12.8.16 后缀增量和减量运算符

一元运算符重载解析应用于选择特定的运算符实现。 以下类型存在预定义的 ++-- 运算符:sbytebyteshortushortintuintnintnuintlongulongcharfloatdoubledecimal 和任意枚举类型。

12.9.2 一元加运算符

预定义的一元加运算符为:

...
nint operator +(nint x);
nuint operator +(nuint x);

12.9.3 一元减运算符

预定义的一元减运算符为:

  • 整数求反:

    ...
    nint operator –(nint x);
    

12.8.16 后缀增量和减量运算符

以下类型存在预定义的 ++-- 运算符:sbytebyteshortushortintuintnintnuintlongulongcharfloatdoubledecimal 和任意枚举类型。

11.7.19 默认值表达式

此外,如果类型是下列值类型之一,则 default_value_expression 是常量表达式:sbytebyteshortushortintuintnintnuintlongulongcharfloatdoubledecimalbool, 或任何枚举类型。

12.9.5 按位求补运算符

预定义的按位补运算符有以下几种:

...
nint operator ~(nint x);
nuint operator ~(nuint x);

12.9.6 前缀增量和减量运算符

以下类型存在预定义的 ++-- 运算符:sbytebyteshortushortintuintnintnuintlongulongcharfloatdoubledecimal 和任意枚举类型。

12.10 算术运算符

12.10.2 乘法运算符

下面列出了预定义的乘法运算符。 所有运算符都计算 xy 的乘积。

  • 整数乘法:

    ...
    nint operator *(nint x, nint y);
    nuint operator *(nuint x, nuint y);
    

12.10.3 除法运算符

下面列出了预定义的除法运算符。 所有运算符都计算 xy 的商。

  • 整数除法:

    ...
    nint operator /(nint x, nint y);
    nuint operator /(nuint x, nuint y);
    

12.10.4 余数运算符

下面列出了预定义的余数运算符。 运算符都计算 xy 之间的除法余数。

  • 整数余数:

    ...
    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 的类型为 intnintlong 时,将丢弃 x 的低序位,其余位向右移动;如果 x 为非负位,则高阶空位位置设置为零;如果 x 为负位,则设置为 1。

    x 的类型为 uintnuintulong 时,x 的低阶位会被丢弃,其余位被右移,高阶空位位置被置零。

  • 无符号右移:

    ...
    nint operator >>>(nint x, int count);
    nuint operator >>>(nuint x, int count);
    

对于预定义的运算符,要移动的位数计算如下:[...]

  • x 的类型为 nintnuint时,移位计数由 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 常量表达式

常量表达式可以是值类型或引用类型。 如果常量表达式是值类型,则它必须是下列类型之一:sbytebyteshortushortintuintnintnuintlongulongcharfloatdoubledecimalbool, 或任何枚举类型。

[...]

隐式常量表达式转换允许 int 类型的常量表达式转换为 sbytebyteshortushortuintnintnuintulong,前提是常量表达式的值在目标类型范围内。

17.4 数组元素访问

数组元素是使用表单 A[I₁, I₂, ..., Iₓ]element_access 表达式访问的,其中 A 是数组类型的表达式,每个 Iₑ 是类型 intuintnintnuintlongulong 或可隐式转换为其中一个或多个类型的表达式。 数组元素访问的结果是一个变量,即索引选择的数组元素。

23.5 指针转换

23.5.1 概述

[...]

此外,在不安全的上下文中,可用的显式转换集被扩展为包括以下显式指针转换:

  • 从任何 pointer_type 到任何其他 pointer_type
  • sbytebyteshortushortintuintnintnuintlongulong 到任何 pointer_type
  • 从任何 指针类型sbytebyteshortushortintuintnintnuintlongulong

23.6.4 指针元素访问

[...] 在一种指针元素访问形式 P[E]中,P 应为指针类型但非 void*的表达式,并且 E 应为一个可以隐式转换为 intuintnintnuintlongulong的表达式。

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* 和一个类型 Nintnuintlong 的表达式ulong,表达式 P + NN + P 计算类型 T* 的指针值,该值是通过将 N * sizeof(T) 添加到 P 给出的地址而得到的。 同样,表达式 P – N 计算出从 T*给定的地址中减去 N * sizeof(T) 后得到的 P 类型的指针值。

各种注意事项

中断性变更

此设计的主要影响之一是 System.IntPtrSystem.UIntPtr 获得一些内置运算符(转换、一元和二进制)。
其中包括 checked 运算符,这意味着这些类型上的以下运算符在溢出时将抛出:

  • IntPtr + int
  • IntPtr - int
  • IntPtr -> int
  • long -> IntPtr
  • void* -> IntPtr

元数据编码

这种设计意味着 nintnuint 可以简单地以 System.IntPtrSystem.UIntPtr 的形式发出,而无需使用 System.Runtime.CompilerServices.NativeIntegerAttribute
同样,在加载元数据 NativeIntegerAttribute 时,可以忽略它。