.NET的基元类型包括哪些?Unmanaged和Blittable类型又是什么?
在讨论.NET的类型系统的时候,我们经常提到“基元类型(Primitive Type)”的概念,我发现很多人并没有真正理解基元类型就究竟包含哪些(比如很多人觉得字符串是基元类型)。除了明确界定基元类型外,本篇文章还会简单介绍额外两种关于类型的概念——Unmanaged类型和Blittable类型。
一、Primitive Type
二、Unmanaged Type
三、Blittable Type
一、Primitive Type
.NET下的基元类型(Primitive Type)如下14个。我们可以这样来记:长度(字节数)分别为1、2、4、8的有/无符号的整数;外加两个基于指针宽度(下x86=4; x64=8)的整数,计10个。长度(字节数)分别为4和8的单精度和双精度浮点数,计2个。外加布尔类型和字符类型, 计2个。所以我们熟悉的String(string)和Decimal(decimal)并不是基元类型。
- 整数(10):Byte(byte)/SByte(sbyte), Int16(short)/UInt16(ushort), Int32(int)/UInt32(uint), Int64(long)/UInt64(ulong), IntPtr(nint)/UIntPtr(nuint)
- 浮点(2):Float(float), Double(double)
- 布尔(1):Boolean(bool)
- 字符(1):Char(char)
对于某个指定的Type对象,我们可以利用它的IsPrimitive属性确定它是否为基元类型。
public abstract class Type
{
public bool IsPrimitive { get; }
}
Type对象的IsPrimitive属性值最终来源于RuntimeTypeHandle类型如下这个内部静态方法IsPrimitive。从该方法的实现和CorElementType的枚举成员也可以看出,枚举值2-13,外加CorElementType.I(IntPtr)和CorElementType.U(UIntPtr)这14个类型属于基元类型的范畴,这与上面的列表是一致的。
public struct RuntimeTypeHandle
{
[SecuritySafeCritical]
internal static bool IsPrimitive(RuntimeType type)
{
CorElementType corElementType = GetCorElementType(type);
if (((int)corElementType < 2 || (int)corElementType > 13) && corElementType != CorElementType.I)
{
return corElementType == CorElementType.U;
}
return true;
}
} [Serializable]
internal enum CorElementType : byte
{
End = 0,
Void = 1,
Boolean = 2,
Char = 3,
I1 = 4,
U1 = 5,
I2 = 6,
U2 = 7,
I4 = 8,
U4 = 9,
I8 = 10,
U8 = 11,
R4 = 12,
R8 = 13,
String = 14,
Ptr = 15,
ByRef = 16,
ValueType = 17,
Class = 18,
Var = 19,
Array = 20,
GenericInst = 21,
TypedByRef = 22,
I = 24,
U = 25,
FnPtr = 27,
Object = 28,
SzArray = 29,
MVar = 30,
CModReqd = 31,
CModOpt = 32,
Internal = 33,
Max = 34,
Modifier = 64,
Sentinel = 65,
Pinned = 69
}
二、Unmanaged Type
顾名思义,Unmanaged类型可以理解不涉及托管对象引用的值类型。如下的类型属于Unmanaged 类型的范畴:
14种基元类型+Decimal(decimal)
枚举类型
指针类型(比如int*, long*)
只包含Unmanaged类型字段的结构体
如果要求泛型类型是一个Unmananged类型,我们可以按照如下的方式使用unmanaged泛型约束。我在《如何计算一个实例占用多少内存?》提到过,只有Unmananged类型采用使用sizeof操作符计算大小。
public static unsafe int SizeOf<T>() where T : unmanaged
{
return sizeof(T);
}
三、Blittable Type
Blittable是站在基于P/Invoke的互操作(InterOp)角度对传递的值是否需要进行转换(Marshaling)而作的分类。Blittable类型要求在托管内存和非托管内存具有完全一致的表示。如果某个参数为Blittable类型,在一个P/Invoke方法调用非托管方法的时候,该参数就无需要作任何的转换。与之类似,如果调用方法的返回值是Blittable类型,在回到托管世界后也无需转换。如下的类型属于Blittable类型范畴:
- 除Boolean(bool)和Char(char)之外的12种基元类型,因为布尔值True在不同的平台可能会表示成1或者-1,对应的字节数可能是1、2或者4,字符涉及不同的编码(Unicode和ANSI),所以这两种类型并非Blittable类型;
- Blittable基元类型的一维数组;
- 采用Sequential和Explicitly布局的且只包含Blittable类型成员的结构或者类,因为采用这两种布局的对象最终会按照一种确定的格式转换成对应的C风格的结构体。如果采用Auto布局,CLR会按照少占用内存的原则对字段成员重新排序,意味着其内存结构是不确定的。
顺便强调一下,DateTime/DateTimeOffset都采用Auto布局(如下所示),Guid虽然是一个默认采用Sequential布局的结构体,但是最终映射在内存种的字节依赖于字节序(Endianness),所以具有这三种类型字段的结构体或者类都不是Blittable类型。
[Serializable]
[StructLayout(LayoutKind.Auto)]
public struct DateTime
{ } [Serializable]
[StructLayout(LayoutKind.Auto)]
public struct DateTimeOffset
{ }
只有Blittable类型的实例才能调用GCHandle的静态方法Alloc为其创建一个Pinned类型的GC句柄。以如下的代码为例,类Foobar的两个属性都是Blittable类型,我们通过标注在类型上的StructLayoutAttribute将布局类型显式设置成Sequential使其称为了一个Blittable类型。
GCHandle.Alloc(new Foobar(), GCHandleType.Pinned); [StructLayout(LayoutKind.Sequential)]
public class Foobar
{
public int Foo { get; set; }
public double Bar { get; set; }
}
如果Foobar类定义成如下的形式,都不能使其称为一个Blittable类型。前者默认采用Auto布局,后者的Bar属性并不是Blittable类型。如果将这样Foobar对象作为参数按照上面的方式调用GCHandle. Alloc方法,会直接抛出ArgumentException异常,并提示“Object contains non-primitive or non-blittable data. (Parameter 'value')”。
public class Foobar
{
public int Foo { get; set; }
public double Bar { get; set; }
} [StructLayout(LayoutKind.Sequential)]
public class Foobar
{
public int Foo { get; set; }
public DateTime Bar { get; set; }
}
.NET的基元类型包括哪些?Unmanaged和Blittable类型又是什么?的更多相关文章
- CLR via C#深解笔记三 - 基元类型、引用类型和值类型 | 类型和成员基础 | 常量和字段
编程语言的基元类型 某些数据类型如此常用,以至于许多编译器允许代码以简化的语法来操纵它们. System.Int32 a = new System.Int32(); // a = 0 a = 1 ...
- C#中的基元类型、值类型和引用类型
C# 中的基元类型.值类型和引用类型 1. 基元类型(Primitive Type) 编译器直接支持的类型称为基元类型.基元类型可以直接映射到 FCL 中存在的类型.例如,int a = 10 中的 ...
- [CLR via C#]基元类型
一.什么是基元类型 某些数据类型如此常用,以至于许多编译器允许代码以简化的语法来操纵它们.例如,可以使用以下语法来分配一个整数: System.Int32 a = new System.Int32() ...
- checked 和 unchecked 基元类型操作
对基元类型执行的许多算术运算都可能造成溢出: Byte b = ; b = (Byte) (b + ); // b 现在包含 44(或者十六进制值 2C) 重要提示:执行上述算术运算时,第一步要求所有 ...
- 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_基元类型(二)
[基元类型推荐] 推荐直接使用 FCL 类型. [理由] 编码时不至于困惑string与String的使用.由于C#的stirng(一个关键字)直接映射到System.String(一个 FCL 类型 ...
- CLR:基元类型、引用类型和值类型
最新更新请访问: http://denghejun.github.io 前言 今天重新看了下关于CLR基元类型的东西,觉得还是有必要将其记录下来,毕竟这是理解CLR成功 之路上的重要一步,希望你也 ...
- 《CLR via C#》读书笔记--基元类型、引用类型和值类型
编程语言的基元类型 编译器直接支持的数据类型称为基元类型.基元类型直接映射到Framework类库中存在的类型.例如:C#中的int直接映射到System.Int32类型.下表给出了C#基元类型与对应 ...
- CLR via C#(02)-基元类型、引用类型、值类型
http://www.cnblogs.com/qq0827/p/3281150.html 一. 基元类型 编译器能够直接支持的数据类型叫做基元类型.例如int, string等.基元类型和.NET框架 ...
- 《CLR via C#》读书笔记(5)基元类型、引用类型和值类型
5.1 基元类型 编译器直接支持的数据类型称为基元类型(primitive type). 以下4行到吗生成完全相同的IL int a = 0; //最方便的语法 System.Int32 b = 0; ...
- 【CLR Via C#】第5章 基元类型、引用类型、值类型
第二遍看这本书,决定记录一下加深印象. 1,基元类型 什么事基元类型?基元类型是直接映射到FrameWork类库(FCL)中存在的类型,编译器直接支持的数据类型.比如int直接映射到System.In ...
随机推荐
- NX二次开发:保存时导出PDF并打开
该工程为在保存时执行开发的功能,函数入口点ufput.其他还有新建.打开.另存等都可以加入开发的操作,具体看UF_EXIT下的介绍. 用户出口是一个可选特性,允许你在NX中某些预定义的位置(或出口)自 ...
- 升级pip报错ERROR: Could not install packages due to an OSError: [WinError 5]
今天在安装python第三方库时,提示pip需要升级,没有多想直接升级,结果报错: 1 ERROR: Could not install packages due to an OSError: [Wi ...
- jq-ajax-get
get有三个参数,第一个时候url,第二个是数据,第三个是回调函数(可以用返回值表示,如下所示) 执行正确时,返回的依次是res,type,xhr. 执行错误连接不上的的依次是xhr,type,res ...
- 快速搭建一个go语言web后端服务脚手架
快速搭建一个go语言web后端服务脚手架 源码:https://github.com/weloe/go-web-demo web框架使用gin,数据操作使用gorm,访问控制使用casbin 首先添加 ...
- Survivor
Survivor (https://codeforces.com/group/L9GOcnr1dm/contest/422378/problem/F) 血的教训 比较有意思的一个贪心题 简单翻译一下题 ...
- Go语言实现基于HTTP的内存缓存服务
所有的缓存数据都存储在服务器的内存中,因此重启服务器会导致数据丢失,基于HTTP通信会将使开发变得简单,但性能不会太好 缓存服务接口 本程序采用REST接口,支持设置(Set).获取(Get)和删除( ...
- ts中抽象类、继承、多态
ts中类抽象类.多态: 抽象类: abstract 修饰, 里面可以没有抽象方法.但有抽象方法(abstract method)的类必须声明为抽象类(abstract class) 多态:父类定义一个 ...
- 获取网络连接状态(二) IsNetworkAlive
IsNetworkAlive需要服务System Event Notification的支持(系统默认自动启动该服务),且需要安装最新的SDK(如.NET) 浏览:MSDN对IsNetworkAliv ...
- 【机器学习与深度学习理论要点】11.什么是L1、L2正则化?
机器学习中几乎都可以看到损失函数后面会添加一个额外项,常用的额外项一般有两种,一般英文称作 L1-norm 和L2-norm,中文称作 L1正则化 和 L2正则化,或者 L1范数 和 L2范数.L1正 ...
- boot-admin整合Liquibase实现数据库版本管理
Liquibase 和 Flyway 是两款成熟的.优秀的.开源/商业版的数据库版本管理工具,鉴于 Flyway 的社区版本对 Oracle 数据库支持存在限制,所以 boot-admin 选择整合 ...