C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

ツリー一括表示

C#でunionを実現する方法 /taro (21/02/16(Tue) 14:53) #96816
Re[1]: C#でunionを実現する方法 /Hongliang (21/02/16(Tue) 15:04) #96818
Re[1]: C#でunionを実現する方法 /魔界の仮面弁士 (21/02/16(Tue) 16:58) #96824
  ├ Re[2]: C#でunionを実現する方法 /taro (21/02/16(Tue) 18:05) #96826
  │└ Re[3]: C#でunionを実現する方法 /Hongliang (21/02/16(Tue) 19:02) #96828
  └ Re[2]: C#でunionを実現する方法 /魔界の仮面弁士 (21/02/16(Tue) 18:35) #96827
    └ Re[3]: C#でunionを実現する方法 /魔界の仮面弁士 (21/02/16(Tue) 19:09) #96829
      └ Re[4]: C#でunionを実現する方法 /魔界の仮面弁士 (21/02/16(Tue) 19:36) #96830
        └ Re[5]: C#でunionを実現する方法 /taro (21/02/19(Fri) 13:43) #96856


親記事 / ▼[ 96818 ] ▼[ 96824 ]
■96816 / 親階層)  C#でunionを実現する方法
□投稿者/ taro (19回)-(2021/02/16(Tue) 14:53:02)

分類:[C#] 

お世話になります。

C言語で以下のような、unionを含む構造体があります。
-----------------------------------
typedef struct{
float a;
float b;
float c;
float d;
}ST_1;

typedef struct{
float value1;
union{
ST_1 structData;
float valueArray[4];
};
}ST_2;
-----------------------------------

「structData.a」は「valueArray[0]」で、「structData.b」は「valueArray[1]」で参照できる、というものですが、
このような構造体定義をC#でかくにはどうすればよいでしょうか?
(そもそも可能でしょうか?)

パッと思い付いた方法としては
-----------------------------------
public struct ST_1
{
public float a;
public float b;
public float c;
public float d;
}

[System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit)]
public struct ST_2
{
[System.Runtime.InteropServices.FieldOffset(0)]
public float value1;

[System.Runtime.InteropServices.FieldOffset(4)]
public ST_1 structData;

[System.Runtime.InteropServices.FieldOffset(4)]
float[] valueArray= new float[4]; // ←NG
}
-----------------------------------
というものですが、当然これはビルドエラーとなります。

よろしくお願いいたします。

[ □ Tree ] 返信 編集キー/

▲[ 96816 ] / 返信無し
■96818 / 1階層)  Re[1]: C#でunionを実現する方法
□投稿者/ Hongliang (1153回)-(2021/02/16(Tue) 15:04:37)
これなら、単純にST_1を以下のいずれかのようにすればいい話かなと思います。
そうすればExplicitの必要すらなくなります。

1. インデクサを実装してインデックス0-3でそれぞれa-dにアクセスする

2. メンバはMarshalAs(UnmanagedType.ByValArray, SizeConst=4)のfloat[]1つだけにして、
 a-dをプロパティにしてこの配列にアクセスする

かたやfloat*4、こなたshort*8、とかならこんな単純にはいきませんが…。
[ 親 96816 / □ Tree ] 返信 編集キー/

▲[ 96816 ] / ▼[ 96826 ] ▼[ 96827 ]
■96824 / 1階層)  Re[1]: C#でunionを実現する方法
□投稿者/ 魔界の仮面弁士 (2966回)-(2021/02/16(Tue) 16:58:40)
No96816 (taro さん) に返信
> 「structData.a」は「valueArray[0]」で、「structData.b」は「valueArray[1]」で参照できる、というものですが、
> このような構造体定義をC#でかくにはどうすればよいでしょうか?

fixed で代用してみるとか。


[StructLayout(LayoutKind.Sequential)]
public struct ST_1
{
 public float a;
 public float b;
 public float c;
 public float d;
}
[StructLayout(LayoutKind.Explicit)]
public unsafe struct ST_2
{
 [FieldOffset(0)] public float value1;
 [FieldOffset(4)] public ST_1 structData;
 [FieldOffset(4)] public fixed float valueArray[4];
}



class Program
{
 static unsafe void Main()
 {
  ST_2 x;
  x.value1 = 1.23f;
  x.structData.a = 2.34f;
  x.structData.b = 3.45f;
  x.structData.c = 4.56f;
  x.structData.d = 5.67f;
  Console.WriteLine(x.valueArray[0]);
  Console.WriteLine(x.valueArray[1]);
  Console.WriteLine(x.valueArray[2]);
  Console.WriteLine(x.valueArray[3]);

  x.valueArray[2] = 6.78f;
  Console.WriteLine($"structData.b = {x.structData.b}");
  Console.WriteLine($"structData.c = {x.structData.c}");
  Console.WriteLine($"structData.d = {x.structData.d}");

  Console.ReadKey();
 }
}
[ 親 96816 / □ Tree ] 返信 編集キー/

▲[ 96824 ] / ▼[ 96828 ]
■96826 / 2階層)  Re[2]: C#でunionを実現する方法
□投稿者/ taro (20回)-(2021/02/16(Tue) 18:05:04)
Hongliang さま
魔界の仮面弁士 さま

ご返信ありがとうございます。
後出しで申し訳ございませんが、今回のご相談は
--------------
・C++で作られたDLLのI/Fに渡す引数が、件の構造体(ST_2)。
・上記DLLを呼び出すアプリはC#で作成。
・アプリ側で「C++のST_2構造体」と同じデータ配置になるような構造体を作成し、それをDLLのI/Fに渡す。
・DLL側は、「structData.a」という形でもアクセスするし、処理の簡素化のために「valueArray[i]」という形でもアクセスする。
・DLL側のコードは変更不可。
--------------
というものです。


>Hongliang さま
ご提示してくださった方法だと、DLL側の修正が必要になる理解ですが、合っておりますでしょうか?


>魔界の仮面弁士 さま
ご提示してくださった方法だとDLL側の修正が不要であると理解しましたので、実際に試してみます。
(試した結果は改めてご報告差し上げますが、すこしお時間いただくかと思います)


[ 親 96816 / □ Tree ] 返信 編集キー/

▲[ 96826 ] / 返信無し
■96828 / 3階層)  Re[3]: C#でunionを実現する方法
□投稿者/ Hongliang (1154回)-(2021/02/16(Tue) 19:02:04)
2021/02/16(Tue) 19:02:52 編集(投稿者)

> ご提示してくださった方法だと、DLL側の修正が必要になる理解ですが、合っておりますでしょうか?
いいえ。
インデクサとかMarshalAsとかで分かるように、すべてC#側のST_1構造体の定義の話です。
[ 親 96816 / □ Tree ] 返信 編集キー/

▲[ 96824 ] / ▼[ 96829 ]
■96827 / 2階層)  Re[2]: C#でunionを実現する方法
□投稿者/ 魔界の仮面弁士 (2967回)-(2021/02/16(Tue) 18:35:59)
No96824 (魔界の仮面弁士) に追記
> fixed で代用してみるとか。

fixed 指定の場合は unsafe が必須になりますので、
プロジェクトのプロパティで、[ビルド]タブの「アンセーフ コードの許可」を
有効にしておいてください。



一応、unsafe が不要なバージョンも書いてみました。
(今回の要件には合わないと思います)

using System;
public struct ST_1
{
 public float A;
 public float B;
 public float C;
 public float D;
}
public readonly ref struct ST_2 // 要 C# 7.2 以上
{
 private readonly Span<float> _span;
 public ref float Value1() => ref _span[0];
 public ref float A => ref _span[1];
 public ref float B => ref _span[2];
 public ref float C => ref _span[3];
 public ref float D => ref _span[4];
 public Span<float> ValueArray => _span[1..5]; // 要 C# 8.0 以上
 public ST_2(Span<float> span) => _span = span;
}

class Program
{
 static void Main()
 {
  ST_2 x = new ST_2(stackalloc float[5] { 1.23f, 2.34f, 3.45f, 4.56f, 5.67f }); // 要 C# 7.3 以上

  Console.WriteLine("☆☆☆ 渡した値は 5 つ ☆☆☆");
  Console.WriteLine($"Value1 = {x.Value1()}");
  Console.WriteLine($"ValueArray[] = {{ {string.Join(", ", x.ValueArray.ToArray())} }}");
  Console.WriteLine($"A = {x.A}");
  Console.WriteLine($"B = {x.B}");
  Console.WriteLine($"C = {x.C}");
  Console.WriteLine($"D = {x.D}");
  Console.WriteLine($"ValueArray[0] = {x.ValueArray[0]}");
  Console.WriteLine($"ValueArray[1] = {x.ValueArray[1]}");
  Console.WriteLine($"ValueArray[2] = {x.ValueArray[2]}");
  Console.WriteLine($"ValueArray[3] = {x.ValueArray[3]}");

  Console.WriteLine();
  Console.WriteLine("☆☆☆ Value1, ValueArray[1], D を書き換え ☆☆☆");
  x.Value1() = -123.45f;
  x.ValueArray[1] = -234.56f;
  x.D = -345.67f;

  Console.WriteLine($"Value1 = {x.Value1()}"); // ★ 1.23 → -123.45
  Console.WriteLine($"ValueArray[] = {{ {string.Join(", ", x.ValueArray.ToArray())} }}"); // ★
  Console.WriteLine($"A = {x.A}");
  Console.WriteLine($"B = {x.B}"); // ★ 3.45 → -234.56
  Console.WriteLine($"C = {x.C}");
  Console.WriteLine($"D = {x.D}"); // ★ 5.67 → -345.67
  Console.WriteLine($"ValueArray[0] = {x.ValueArray[0]}");
  Console.WriteLine($"ValueArray[1] = {x.ValueArray[1]}"); // ★ 3.45 → -234.56
  Console.WriteLine($"ValueArray[2] = {x.ValueArray[2]}");
  Console.WriteLine($"ValueArray[3] = {x.ValueArray[3]}"); // ★ 5.67 → -345.67

  Console.ReadKey();
 }
}
[ 親 96816 / □ Tree ] 返信 編集キー/

▲[ 96827 ] / ▼[ 96830 ]
■96829 / 3階層)  Re[3]: C#でunionを実現する方法
□投稿者/ 魔界の仮面弁士 (2968回)-(2021/02/16(Tue) 19:09:54)
No96827 (魔界の仮面弁士) に追記
> 一応、unsafe が不要なバージョンも書いてみました。

ST_1 を使うのを忘れていたので書き直し。(structData の階層が抜けてました!)

fixed 版と比べると元コードからの変更点が多くなるので、
今回の目的とは合わないと思いますが。

using System;
public readonly ref struct ST_1
{
 private readonly Span<float> _span;
 public ref float A => ref _span[0];
 public ref float B => ref _span[1];
 public ref float C => ref _span[2];
 public ref float D => ref _span[3];
 public ST_1(Span<float> span) => _span = span;
}
public readonly ref struct ST_2
{
 private readonly Span<float> _span;
 public ref float Value1() => ref _span[0];
 public readonly Span<float> ValueArray => _span[1..5];
 public readonly ST_1 StructData => new ST_1(_span[1..5]);
 public ST_2(Span<float> span) => _span = span;
}

class Program
{
 static void Main()
 {
  ST_2 x = new ST_2(stackalloc float[5] { 1.23f, 2.34f, 3.45f, 4.56f, 5.67f });

  Console.WriteLine("☆☆☆ 渡した値は 5 つ ☆☆☆");
  Console.WriteLine($"Value1 = {x.Value1()}");
  Console.WriteLine($"ValueArray[] = {{ {string.Join(", ", x.ValueArray.ToArray())} }}");
  Console.WriteLine($"StructData.A = {x.StructData.A}");
  Console.WriteLine($"StructData.B = {x.StructData.B}");
  Console.WriteLine($"StructData.C = {x.StructData.C}");
  Console.WriteLine($"StructData.D = {x.StructData.D}");
  Console.WriteLine($"ValueArray[0] = {x.ValueArray[0]}");
  Console.WriteLine($"ValueArray[1] = {x.ValueArray[1]}");
  Console.WriteLine($"ValueArray[2] = {x.ValueArray[2]}");
  Console.WriteLine($"ValueArray[3] = {x.ValueArray[3]}");

  Console.WriteLine();
  Console.WriteLine("☆☆☆ Value1, ValueArray[1], StructData.D を書き換え ☆☆☆");
  x.Value1() = -123.45f;
  x.ValueArray[1] = -234.56f;
  x.StructData.D = -345.67f;

  Console.WriteLine($"Value1 = {x.Value1()}"); // ★ 1.23 → -123.45
  Console.WriteLine($"ValueArray[] = {{ {string.Join(", ", x.ValueArray.ToArray())} }}"); // ★
  Console.WriteLine($"StructData.A = {x.StructData.A}");
  Console.WriteLine($"StructData.B = {x.StructData.B}"); // ★ 3.45 → -234.56
  Console.WriteLine($"StructData.C = {x.StructData.C}");
  Console.WriteLine($"StructData.D = {x.StructData.D}"); // ★ 5.67 → -345.67
  Console.WriteLine($"ValueArray[0] = {x.ValueArray[0]}");
  Console.WriteLine($"ValueArray[1] = {x.ValueArray[1]}"); // ★ 3.45 → -234.56
  Console.WriteLine($"ValueArray[2] = {x.ValueArray[2]}");
  Console.WriteLine($"ValueArray[3] = {x.ValueArray[3]}"); // ★ 5.67 → -345.67

  Console.ReadKey();
 }
}
[ 親 96816 / □ Tree ] 返信 編集キー/

▲[ 96829 ] / ▼[ 96856 ]
■96830 / 4階層)  Re[4]: C#でunionを実現する方法
□投稿者/ 魔界の仮面弁士 (2969回)-(2021/02/16(Tue) 19:36:35)
No96829 (魔界の仮面弁士) に追記
> ST_1 を使うのを忘れていたので書き直し。(structData の階層が抜けてました!)

「struct + インスタンス フィールド」を
「class + インスタンス プロパティ」に置き換えたバージョン。


using System;
public class ST_1
{
 private readonly float[] _span = new float[4];
 public float a { get => _span[0]; set => _span[0] = value; }
 public float b { get => _span[1]; set => _span[1] = value; }
 public float c { get => _span[2]; set => _span[2] = value; }
 public float d { get => _span[3]; set => _span[3] = value; }
 public ST_1(float a, float b, float c, float d) => (this.a, this.b, this.c, this.d) = (a, b, c, d);
 public ST_1(float[] f) : this(f[0], f[1], f[2], f[3]) { }
 public ST_1() : this(0f, 0f, 0f, 0f) { }
}
public class ST_2
{
 public float value1 { get; set; }
 public ST_1 structData { get => new ST_1(valueArray); }
 public float[] valueArray { get; init; }
}

class Program
{
 static void Main()
 {
  ST_2 x = new ST_2 { value1 = 1.23f, valueArray = new float[] { 2.34f, 3.45f, 4.56f, 5.67f } };

  Console.WriteLine($"value1 = {x.value1}");
  Console.WriteLine($"valueArray[] = {{ {string.Join(", ", x.valueArray)} }}");
  Console.WriteLine($"structData.a = {x.structData.a}");
  Console.WriteLine($"structData.b = {x.structData.b}");
  Console.WriteLine($"structData.c = {x.structData.c}");
  Console.WriteLine($"structData.d = {x.structData.d}");
  Console.WriteLine($"valueArray[0] = {x.valueArray[0]}");
  Console.WriteLine($"valueArray[1] = {x.valueArray[1]}");
  Console.WriteLine($"valueArray[2] = {x.valueArray[2]}");
  Console.WriteLine($"valueArray[3] = {x.valueArray[3]}");

  Console.WriteLine();
  Console.WriteLine("☆☆☆ value1, valueArray[1], structData.d を書き換え ☆☆☆");
  x.value1 = -123.45f;
  x.valueArray[1] = -234.56f;
  x.structData.d = -345.67f;

  Console.WriteLine($"value1 = {x.value1}"); // ★ 1.23 → -123.45
  Console.WriteLine($"valueArray[] = {{ {string.Join(", ", x.valueArray)} }}"); // ★
  Console.WriteLine($"structData.a = {x.structData.a}");
  Console.WriteLine($"structData.b = {x.structData.b}"); // ★ 3.45 → -234.56
  Console.WriteLine($"structData.c = {x.structData.c}");
  Console.WriteLine($"structData.d = {x.structData.d}"); // ★ 5.67 → -345.67
  Console.WriteLine($"valueArray[0] = {x.valueArray[0]}");
  Console.WriteLine($"valueArray[1] = {x.valueArray[1]}"); // ★ 3.45 → -234.56
  Console.WriteLine($"valueArray[2] = {x.valueArray[2]}");
  Console.WriteLine($"valueArray[3] = {x.valueArray[3]}"); // ★ 5.67 → -345.67

  Console.ReadKey();
 }
}
[ 親 96816 / □ Tree ] 返信 編集キー/

▲[ 96830 ] / 返信無し
■96856 / 5階層)  Re[5]: C#でunionを実現する方法
□投稿者/ taro (21回)-(2021/02/19(Fri) 13:43:03)
Hongliang さま
魔界の仮面弁士 さま

重ね重ねありがとうございます。
ご提示くださった方法をどちらも試してみて、より高速な方で実装していこうと考えております。

・・・が、少し体調を崩しておりすぐの確認が難しそうです。
必ず確認して結果をご報告差し上げますのでご容赦ください。


[ 親 96816 / □ Tree ] 返信 編集キー/


管理者用

- Child Tree -