[C#] C/C++相当の共用体(union)を扱う
C/C++ではメンバのメモリ領域を共有できる共用体が存在しますが、C#には存在しません。
しかし、属性を駆使して共用体を実装することは可能です。
具体的なコードは以下のようになります。
[System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit)] struct TestUnion { [System.Runtime.InteropServices.FieldOffset(0)] public int i; [System.Runtime.InteropServices.FieldOffset(0)] public double d; [System.Runtime.InteropServices.FieldOffset(0)] public char c; [System.Runtime.InteropServices.FieldOffset(0)] public byte b; }
structの頭に付けている属性[System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit)]はstructを共用体として定義するためのおまじないと思って良いでしょう。
これは外部のアンマネージコード側からC#のクラスを扱いたい場合などにフィールドのオフセットを厳密に指定したい場合などに使用します。
フィールドのオフセットは[System.Runtime.InteropServices.FieldOffset(0)]で指定しています。
0の部分がオフセット値となり、構造体の先頭アドレスを指します。
すべてのフィールドにこの属性記述をしているので、これらが先頭アドレスに配置されることになります。
まさに共用体そのものですね。
この構造体に値を入れて表示すると以下のようになります。
■サンプル
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Runtime.InteropServices; namespace UnionTest { // 共用体 [System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit)] struct TestUnion { [System.Runtime.InteropServices.FieldOffset(0)] public int i; [System.Runtime.InteropServices.FieldOffset(0)] public double d; [System.Runtime.InteropServices.FieldOffset(0)] public char c; [System.Runtime.InteropServices.FieldOffset(0)] public byte b; } class Program { static void Main(string[] args) { // 値を代入して表示 TestUnion tu = new TestUnion(); tu.i = 123; System.Console.WriteLine(tu.i); tu.d = 0.0; System.Console.WriteLine(tu.i); tu.c = 'A'; System.Console.WriteLine(tu.i); tu.c = 'B'; System.Console.WriteLine(tu.i); } } }
■実行結果
123 0 65 66
共用体の実装が煩雑になるのが残念ですが、使う側は今まで通りの記述でできるので良いですね。
■参考サイト
方法: 属性を使用して C/C++ の共用体を作成する (C# および Visual Basic)
LayoutKind 列挙体 (System.Runtime.InteropServices)
アンマネージドコードとは 「アンマネージコード」 (unmanaged code): – IT用語辞典バイナリ