当前位置: 代码迷 >> 综合 >> C# 经典实例 第一章 类和泛型 #1.1 创建联合类型的结构
  详细解决方案

C# 经典实例 第一章 类和泛型 #1.1 创建联合类型的结构

热度:14   发布时间:2024-02-19 17:16:00.0

问题:

需要创建一种数据类型,其行为方式类似于C++中的联合类型。联合类型主要用于互操作场景,其中非托管代码接受和/或返回一个联合类型。(建议不要在其他情况下使用它)

解决方案:

使用一个结构,并用StructLayout特性标记他(在构造函数中指定LayoutKind.Explicit布局类型)。此外,利用FieldOffset特性标记结构中的每个字段。

下面的结构定义了一个联合类型,其中可以存储一个带符号数值。

using System.Runtime.InteropServices;[StructLayoutAttribute(LayoutKind.Explicit)]
struct SignedNumber
{[FieldOffsetAttribute(0)] public sbyte Num1;[FieldOffsetAttribute(0)]public short Num2;[FieldOffsetAttribute(0)]public int Num3;[FieldOffsetAttribute(0)] public long Num4;[FieldOffsetAttribute(0)] public float Num5;[FieldOffsetAttribute(0)] public double Num6;
}

下一个结构类似于SignedNumber结构,不同之处是除了带符号的数值之外,它还可以包含String类型:

using System.Runtime.InteropServices;[StructLayoutAttribute(LayoutKind.Explicit)]
struct SignedNumberWithText
{[FieldOffsetAttribute(0)]public sbyte Num1;[FieldOffsetAttribute(0)]public short Num2;[FieldOffsetAttribute(0)]public int Num3;[FieldOffsetAttribute(0)]public long Num4;[FieldOffsetAttribute(0)]public float Num5;[FieldOffsetAttribute(0)]public double Num6;[FieldOffsetAttribute(16)]public string Text1;
}

讨论:

联合类型是一种C++代码中较为常见的结构类型。不过,有一种方式可以使用C#中的结构数据类型来复制其结构。联合(union)是一种结构,在内存中的特定位置为该结构接受多种类型。例如,SignedNumber结构是使用C#结构创建的一个联合类型的结构。这种结构可以接受任何类型的带符号的数值类型(sbyte、int和long等),但它只在结构中的同一个位置(同一偏移量)接受这种数字类型。

(由于StructLayoutAttribute可以同时应用于结构和类,在创建联合数据类型时也可以使用类)

注意FieldOffsetAttribute将值0传递给它的构造参数。这表明这个字段距离结构开始处的偏移量为0字节。可以将这个特性与StructLayoutAttribute结合使用,手动强制指定结构中的字段开始于什么位置(即每个字段在内存中相对于这个结构开始处的偏移量)。FieldOffsetAttribute只能与设置为LayoutKind.Explicit的StructLayoutAttribute一起使用。此外,它不能用于结构内的静态成员。

联合类型可能会带来一些问题,因为几种类型实质上是相互叠加在一起的。最大的问题是如何从联合类型结构中提取正确的数据类型。思考一下,如果你选择在SignedNumber结构中存储long数值类型的值long.MaxValue,会发生什么情况。随后,你可能会偶然尝试从这个结构中提取一个byte数据类型值。这样操作,你将会取回这个long值中的第一个字节。

另一个问题是在正确的偏移位置开始字段。SignedNumberWithText联合类型在偏移量为0的位置叠加了大量带符号的数值数据类型。这个结构中的最后一个字段位于内存中距离这个结构开始处偏移量为16字节的位置。如果你意外的把字符串字段Text1覆盖在任何其他带符号的数值数据类型上,在运行时将得到一个异常。基本规则是:允许你把一个值类型叠加在另一种值类型之上,但是不能把一种引用类型叠加与一种值类型之上。如果用以下特效标记Text1字段:

 [FieldOffsetAttribute(14)]
就会在运行时引发下面这个异常(注意,编译器不会捕捉到这个问题)
    System.TypeLoadException: Could not load type 'SignedNumberWithText' from      assembly 'CSharpRecipes, Version=1.0.0.0, Culture=neutral,      PublicKeyToken=fe85c3941fbcc4c5' because it contains an object field at      offset 14 that is incorrectly aligned or overlapped by a non-object field.
在C#中使用复杂的联合类型时,必须保证正确的偏移量。

参考:

MSDN文档中的“StructLayoutAttribute类“主题。

  相关解决方案