2016-06-16 4 views
10

ちょうど不思議なことに、構造体/値型のサイズをC#の急激な変更に変更していますか? structを変更すると配列/その他の構造体のサイズに直接影響するため、構造体はメモリレイアウトの面でより敏感です。それが使用するライブラリ内の構造体のレイアウトが変更された後、バイナリまたはソースのいずれかで壊れるコードの例はありますか?構造体のサイズをC#の急激な変更に変更していますか?

注:「ブレーク」とは、コンパイルに失敗したか、ILが無効になったことを意味します。たとえば、私はこれを壊滅的な変化とは考えていません。まだ実行されているので、これは改変とはみなされません。

// My.Library v1 
public struct MyStruct {} 

// My.Library v2 
public struct MyStruct { int _field; } 

// App code 
using My.Library; 
using System.Runtime.InteropServices; 

Console.WriteLine(Marshal.SizeOf<MyStruct>()); // before printed 1, now prints 4 

+1

InteropServicesを使用してアンマネージコードと対話していますか?答えがイエスならば、それは急変です。いいえの場合、答えはより微妙です。 –

+0

申し訳ありませんが、私はあなたの質問を理解していない...あなたはアセンブリを再コンパイルしたので、すべてがOKになるはずですか? –

+0

ジッタは多くの罪を隠します。構造体サイズは、ビルド時には役割を果たさず、ランタイムのみです。初期化されていないフィールドを持つことは驚くかもしれません。 –

答えて

8

フィールドを追加してサイズを変更すると、厳密に管理されたコードで問題ありません。

フィールドを追加することは、コードが新しいタイプで再JITされ、すべての割り当てが正しいサイズを使用するため、大きな変更ではありません。値型であるため、新しいフィールドは空の値で適切に初期化されます。

既存のフィールドまたはプロパティのタイプを削除/変更することは間違いなく変更を破ることです。

値タイプは封印されているので、他のライブラリはそのタイプから派生することはできません。クラスとは異なり、「この派生クラスは新しい仮想プロパティ/インタフェースメソッドを実装しませんでした。

注:interopやその他の種類のバイナリシリアル化にコントロールの外部で値の型を使用すると、変更が破損する場合があります。

I.e.他の誰かがMyLib.Point {int x;int y;}を使用して、バイナリのシリアライゼーションを伴うポイントのリストをファイルに保存しました。現在 "MyLib"がシリアル化されたデータより新しいフィールドをMyLib.Pointに追加すると、もはやバイナリシリアル化で読み取ることができなくなります。ネイティブinteropと同様の問題。

3

はい、厳密に管理されたコードであっても、新しいフィールドを追加すると、ソースコードの非互換性が可能です。あなたの例を取ると、これはバージョン1ではなく、バージョン2でコンパイル:

MyStruct s; 
Console.WriteLine(s); 

理由は、C#はすべてのフィールドに値が割り当てられている場合は、構造体のローカルを使用できることです。バージョン1では、フィールドがないため、sが「確実に割り当てられています」。しかし、フィールドがバージョン2で追加された場合でも、それがプライベートであっても、sはもはや確実に割り当てられていないため、コンパイルされません。

このケースは、CLRがフィールドの初期値への初期化を保証するため、バイナリ互換でなければなりません。

Jared Parsonsは、プライベートな実装の詳細を変更することが危険な(安全でないコードの場合)、または中断するようなケースについて詳しく説明している構造体のプライベートフィールドの件に関してgood blog postを持っていました。

+0

Upvoted。ちなみに、ブログ記事のおかげで、面白い読み物でした。 –

関連する問題