9

実際の例外を確認するには、64ビットマシンが必要です。問題を再現するダミークラスをいくつか作成しました。

[StructLayout(LayoutKind.Sequential, Pack = 1)]
    public class InnerType
    {
        char make;
        char model;
        UInt16 series;
    }

 [StructLayout(LayoutKind.Explicit)]
    public class OutterType
    {
        [FieldOffset(0)]
        char blah;

        [FieldOffset(1)]
        char blah2;

        [FieldOffset(2)]
        UInt16 blah3;

        [FieldOffset(4)]
        InnerType details;
    }

    class Program
    {
        static void Main(string[] args)
        {
            var t = new OutterType();
            Console.ReadLine();
        }
    }

これを64clrで実行すると、タイプロード例外が発生します。

System.TypeLoadException was unhandled 
  Message="Could not load type 'Sample.OutterType' from assembly 'Sample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 4 that is incorrectly aligned or overlapped by a non-object field."

ターゲットCPUを32に強制すると、正常に動作します。

また、InnerTypeをクラスから構造体に変更すると、それも機能します。誰かが何が起こっているのか、私が間違っているのかを説明できますか?

ありがとう

4

5 に答える 5

20

ここでは、重複する型に関する部分が誤解を招きます。問題は、.Net 参照型では常にポインター サイズの境界に揃える必要があることです。フィールドオフセットは32ビットシステムのポインターサイズである4バイトであるため、ユニオンはx86で機能しますが、8の倍数をオフセットする必要があるため、x64では失敗します。オフセットを3またはx86 プラットフォームでは 5。

編集:疑いのある人のために-インターネットですぐに参照できるものを見つけることができませんでしたが、Serge LidinによるExpert .NET 2.0 IL Assemblerの175ページをチェックしてください。

于 2009-01-19T17:54:10.653 に答える
3

また、char データ型を 1 バイトにパックしていることにも気付きました。.NET の Char 型のサイズは 2 バイトです。これが実際の問題かどうかは確認できませんが、再確認します。

于 2009-01-22T05:15:22.493 に答える
0

それ自体がLayoutindである他の構造体内に構造体を配置したい場合。異なるビットネスモード(または異なるパッキング要件のマシン)で動作することが期待される場合は、明示的なサイズ値(バイト単位)を使用する必要があります。 「順番に並べて、内部に詰め込むのではなく、最後に好きなだけスペースを使う」というものがあります。サイズを指定しない場合、ランタイムは自由に必要なだけスペースを追加できます。

一般に、構造体とオブジェクトタイプをオーバーラップさせることを拒否する理由は、GCルーチンがライブオブジェクトグラフを自由にトラバースする必要があるためです。これを実行している間、結合された(重複する)フィールドがオブジェクト参照として意味があるのか​​、生のビット(たとえば、intまたはfloat)として意味があるのか​​を知ることはできません。正しく動作するには、すべてのライブオブジェクト参照をトラバースする必要があるため、ヒープ内の任意の場所(またはヒープ外)を指す可能性のある「ランダム」ビットをトラバースすることになります。

32/64参照は、実行時間に応じて32ビットまたは64ビットを使用するため、Explictを使用する必要があります。参照を含む結合参照と、値型を含む値型のみです。参照型が異なる場合は、両方のターゲットプラットフォームの境界に揃えてください(注:ランタイムに依存します)および以下のいずれかを実行します。

  1. すべての参照フィールドが構造体の最後のエントリであることを確認してください。その後、ランタイム環境のビット数に応じて、構造体を自由に大きく/小さくすることができます。
  2. 32ビット環境でも64ビット環境でも、すべてのオブジェクト参照で64ビットを消費するように強制します

アラインメントに関する注意:アラインメントされていない参照フィールドでエラーが発生しました。構造体で何らかのアクションを実行しない限り、コンパイラは型ロードを削除しました。

[StructLayout(LayoutKind.Explicit)]
public struct Foo
{
    [FieldOffset(0)]
    public byte padding;
    [FieldOffset(1)]
    public string InvalidReference;
}

public static void RunSnippet()
{
    Foo foo;
    foo.padding = 0;
    foo.ValidReference = "blah";
    // Console.WriteLine(foo); // uncomment this to fail
}

関連する詳細は、ECMA仕様http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdfにあります。&を含むネイティブサイズ値の調整を義務付けるセクション16.6.2を参照してください。必要に応じてこれを回避するために、整列されていないプレフィックス命令が存在することに注意してください。

ただし、モノラル(OSXIntelとWin32Intel 32ビットの両方)では、上記のコードは機能します。ランタイムはレイアウトを尊重せず、黙って「修正」するか、任意の配置を許可します(歴史的に、この点でMSランタイムよりも柔軟性がありませんでした)。monoによって生成されたCLI中間形式には、.unaligned命令プレフィックスが含まれていないため、仕様に準拠していないように見えます。

それは私にモノラルだけをチェックすることを教えてくれます。

于 2009-01-19T17:33:14.750 に答える
0

私は同じ問題に苦労し、MSDN でこのトピックに関する明確な参照が見つからないことを嫌っていました。ここで回答を読んだ後、私は .NET の x86 と x64 の違いに集中し始め、次のことを見つけました: Migrating 32-bit Managed Code to 64-bit . ここでは、ポインターが x86 では 4 バイト、x64 では 8 バイトであることが明確に示されています。他の人に役立つことを願っています。

ちなみに、Stack Overflow には関連する質問がたくさんあります。そのうちの 2 つを追加しますが、他の興味深い点について言及しています。

于 2012-05-31T14:52:59.443 に答える
-2

CLS に準拠していないため、Uint16 に問題がある可能性があります (こちらを参照してください: http://msdn.microsoft.com/en-us/library/system.uint16.aspx ) 。

于 2009-01-19T08:08:18.120 に答える