2016-06-17 5 views
3

私はそれが構造体のアンマネージ表現に影響documentation for [FieldOffset]によるC位.NETのユニオンフィールド - 実際にマネージコードで作業できますか?

[StructLayout(LayoutKind.Explicit)] 
public struct MyUnion 
{ 
    [FieldOffset(0)] 
    public string MyString; 
    [FieldOffset(0)] 
    public Version MyVersion; 
} 

このような構造体を定義しました。しかし私の驚いたことに、マネージコードでもうまくいくように思えます。dotTraceでメモリ使用量をプロファイルすると、各MyUnionインスタンスは1つのポインタ(x64の8バイト)のサイズです!値はまだ完全に安全であるようです。

var stringInside = new MyUnion { MyString = "The string" }; 
var versionInside = new MyUnion { MyVersion = new Version(1, 2, 3, 4) }; 
Console.WriteLine(stringInside.MyString); // The string 
Console.WriteLine(versionInside.MyVersion); // 1.2.3.4 

間違ったフィールドにアクセスするとどうなりますか?

var whatIsThis = stringInside.MyVersion; 
var andThis = versionInside.MyString; 
Console.WriteLine("{0} (type = {1})", whatIsThis, whatIsThis.GetType().FullName); // The string (type = System.String) 
Console.WriteLine("{0} (type = {1})", andThis, andThis.GetType().FullName); // 1.2.3.4 (type = System.Version) 

まだこの例えば、含まれるオブジェクトの実際の型が保存されているという意味で「作品」が、もちろん何のコンパイラが考え、どのようなランタイムは思っ間の切断が今そこにあります

Console.WriteLine("Compiler: is it a string? {0}", versionInside.MyString is string); // True 
Console.WriteLine("Runtime: is it a version? {0}", versionInside.MyString.GetType() == typeof(Version)); // True 

このような組合を使用することはどのくらい危険ですか?私はここに見られる行動に頼ることができますか?それは他の方法で壊れる可能性はありますか?特に、このようなコードを使用するのは安全ですか?

if (versionInside.MyString.GetType() == typeof(string)) 
{ 
    Console.WriteLine("OK, it's a string, use the MyString field"); 
} 
else 
{ 
    Console.WriteLine("OK, it's a Version, use the MyVersion field"); 
} 
+0

従来、組合の目的は記憶を節約することです。これは実際問題ですか?今日の複雑さ/難読化に値するでしょうか? –

+1

はい、もちろんです。十分なデータがある場合、メモリは常に問題になります。 – EM0

+0

それをうまく文書化してください - あなたはこれを驚かせていくつかの開発者を捕まえます(とにかく50歳未満の人)。 –

答えて

2

これは問題ありません。サポートされていない唯一のシナリオは、値タイプフィールドと参照タイプフィールドをオーバーラップさせることです。これで、GCは、値にオブジェクト参照が含まれているかどうかを確実に判断できなくなりました。 CLRは緊急停止を早期に解除し、TypeLoadExceptionを取得します。

このようなユニオンのより一般的な形式は、discriminated unionです。 variant typeは標準的な例です。フィールドのタイプを示す別のフィールドがあります。実際にはあなたの例でこれをすでに持っています。すべてのオブジェクトは、その型を示す隠されたフィールドを持っています。 "型ハンドル"または "メソッドテーブルポインタ"として知られています。 Object.GetType()はそれを使用します。ガベージコレクタがオブジェクトの実際の型を発見するために使用するフィールドは、宣言された型が基本クラスまたはインタフェースである可能性があるため、有用ではありません。

2つの値の型の値を重複させると、必然的に問題に遭遇します。今度は、別のフィールドがないと実際の型を知ることはできません。あなたが間違ったものを使うと、ゴミを読むだけです。書き込みはメモリの破損を引き起こすことはできません、構造は最大の型を含むのに十分な大きさです。そのようなトラブルは、診断や予測が難しいことは決してありません。

+0

予期しない参照型を別の型の変数に詰め込むことによって、ランタイムのどの部分も混乱しないと確信していますか? – usr

+1

自分で試してみてください。 –

+1

君主は力強いランタイムの道を見極めるのを助ける。 – usr

関連する問題