.NET的基元类型包括哪些?Unmanaged和Blittable类型又是什么?

news/2024/10/30 23:19:30/

在讨论.NET的类型系统的时候,我们经常提到“基元类型(Primitive Type)”的概念,我发现很多人并没有真正理解基元类型就究竟包含哪些(比如很多人觉得字符串是基元类型)。除了明确界定基元类型外,本篇文章还会简单介绍额外两种关于类型的概念——Unmanaged类型和Blittable类型。

一、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基元类型的一维数组;
  • 采用SequentialExplicitly布局的且只包含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; }
}


http://www.ppmy.cn/news/396719.html

相关文章

小米手机关闭自带服务器,小米手机耗电大?关闭这两个设置,小米手机能更省电!...

原标题&#xff1a;小米手机耗电大&#xff1f;关闭这两个设置&#xff0c;小米手机能更省电&#xff01; 小米手机耗电大、续航差是很多小米用户最头疼的事&#xff0c;明明刚充满的电&#xff0c;才玩了一会儿就去掉了一大半&#xff0c;真的很忧伤~ 那么怎么做才能让小米更省…

手机充不进电什么原因怎么办

品牌型号&#xff1a;iPhone 13 pro 系统&#xff1a;iOS 16.1.1 手机充不上电什么原因 手机充不上电原因&#xff1a; 1、电压不够&#xff1a;建议换个插口&#xff0c;或者把手机的屏幕亮度调到最暗&#xff0c;然后重新插上充电器。 2、检查室温是不是过低&#xff1a;部…

如何查看手机电量消耗是否正常?

【查看方法】 1、手机自带有电量使用信息查询&#xff0c;进入方式&#xff1a;设置——电池&#xff0c;见下图&#xff1a; 这个界面可以查看各应用耗电信息&#xff0c;还可以查看各应用耗电排行。 2、点击“电量使用详情”&#xff0c;进入如下界面&#xff1a; 该界面显示…

手机边充电边玩,消耗的电量是来自电池还是充电器呢?

如今人们最离不开的是手机&#xff0c;从睁开眼&#xff0c;手机就是他的全部&#xff0c;在地铁上、公交上&#xff0c;随处可见一些低头族专注于玩手机&#xff0c;甚至大部分的人都是边充电边玩&#xff0c;那么很多人又会存在这样的疑问&#xff1a;手机边充电边玩&#xf…

小米八android耗电比例很大,小米手机耗电太快?MIUI系统最全的省电方法,解决手机耗电问题...

原标题&#xff1a;小米手机耗电太快&#xff1f;MIUI系统最全的省电方法&#xff0c;解决手机耗电问题 MIUI10系统作为小米8周年发布会上的一个重磅产品&#xff0c;一发布就有不少小米完成升级。随着近期的MIUI10稳定版的推出&#xff0c;并且支持多款机型升级尝鲜&#xff0…

如何正确给手机充电?

如何正确给手机充电&#xff1f; 给手机充电的最好方式是一有机会就充电&#xff0c;每次充一点电。哪怕只是几分钟也行&#xff0c;零星时间充电不会损害电池。 不要等电池电量完全耗尽后再给手机充电。所谓的“深度放电”&#xff0c;即等电池电量几乎耗尽时再给它充电&…

小米八android耗电比例很大,MIUI系统最全省电方法,五招解决小米手机耗电快

相比功能机时代&#xff0c;现在智能手机已经深入到了人们的实际工作生活中&#xff0c;但是一款硬件配置功能再怎样强悍的手机如果遇到电池没电都等于0&#xff0c;甚至是让人感到非常焦虑的事。所以&#xff0c;对于苹果iPhone也好我国市场各种智能手机牌子也罢&#xff0c;续…