C# 創建 union 結構


使用C#創建聯合結構體

問題

想要用C#創建一種數據類型,類似於 C/C++ 中的聯合(union)類型。聯合類型主要用於互操作場景,其中非托管代碼接受或返回一個聯合類型。

解決辦法

使用一個結構,並用 [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 類型。

[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;
}

若非必須,不建議使用這種類似於union類型的結構體。

講解

聯合類型是一種在 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: 不能從程序集加載類型'SignedNumberWithText', 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# 中使用復雜的聯合類型時,一定要保證正確的偏移量。

參考資料:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM