12

かなりの調査を行いましたが、なぜこのエラーがまだ発生するのかについて、今は行き詰っています。次の属性を持つ構造体があります。

struct Account
{
    //private attributes
    private double mBalance;
    private int mAccountNumber;
    private string mName;
    private string mDateCreated;
}

そして、次のことをしようとしています:

class BankManager
{
    //private attributes
    private unsafe Account *mAccounts;
    private unsafe bool *mAccountsAvailable;
    private int mNumberAccounts;
}

クラス Account を構造体に変更し、クラス BankManager の属性に「unsafe」を使用し、安全でないコードを使用できることをコンパイラーに伝えた後でも (プロパティ -> ビルド)、まだこのエラーが発生します。

*mAccounts

理由についてのアイデアはありますか?構造体で使用しているすべての型は、C# でポインターを持つことが合法であると確信しています。前もって感謝します!

4

5 に答える 5

27

Account クラスの文字列がこの問題の原因です。その理由を理解するには、ガベージ コレクターのしくみを理解する必要があります。オブジェクトへの参照を追跡することでガベージを発見します。mName と mDateCreated はそのような参照です。mBalance と mAccountNumber は ではありません。これらのフィールドは値の型です。そして、最も重要なことは、BankManager.mAccounts フィールドはポインターではなく、ポインターであることです。

そのため、コンパイラは、ガベージ コレクターが文字列参照を認識できないことを前もって伝えることができます。これを行う唯一の方法は、mAccount フィールドを通過することであり、参照ではないためです。

これに対する唯一の解決策は、厳密に値型に限定することです。文字列に対してこれを行う唯一の方法は、たとえば、Marshal.StringToCoTaskMemUni() を使用してそれらをアンマネージメモリに割り当て、フィールドに IntPtr を格納することです。ガベージ コレクターの手の届かないところにあり、移動できません。また、その文字列を解放する負担も発生します。

明らかに、これは実用的ではなく、C プログラムで非常に一般的な種類の問題であるリークが発生しやすくなります。なぜこれを追求しているのかはまったくわかりませんが、オブジェクトへの参照はすでに単純なポインターであるため、ポインターを自分で使用しても何も得られないことに注意してください。

于 2012-11-09T00:44:11.607 に答える
3

文字列は .NET の参照型であり、構造体ポインターに対して blittable ではありません。やりたいことの値の型のリストについては、Blittable と Non-Blittable の型を参照してください。

特別なビジネス要件がない限り、保守性と一般的な健全性のためにマネージド メモリを使用する必要があります。

于 2012-11-08T22:57:01.863 に答える
1

使用private unsafe fixed char mName[126];
文字列は管理対象タイプであり、固定されていない配列も同様です。

于 2012-11-08T22:52:48.853 に答える
0

コピーするコレクターは物事を移動できるため、管理されたデータは固定された場所にとどまりません。これは、管理されたボックス化された値の型にも同様に当てはまります。管理されたボックス化されていない値の型は、スタック上または他のオブジェクト内にのみ存在できます。それらがスタック内にある場合、それらは固定位置のみを持ちます。

引き続き有効なポインターを取得できる固定位置を持つヒープ割り当て構造体を作成するには、それをアンマネージメモリに割り当てる必要があります。ただし、一度アンマネージ メモリに割り当てると、ガベージ コレクターはそれらのポインターを認識しないため、マネージド ポインターをそこに配置することはできません (別名、文字列を使用できません)。圧縮中に管理対象オブジェクトを移動します。

たとえば、これは有効な (必ずしも良いとは限りませんが) ことです。

[StructLayout(LayoutKind.Sequential, Pack=1)]
public unsafe struct Account {
    public int a;
    public char* mName;
}
public class BankManager {
    private unsafe Account* mAccounts;
    public unsafe int GetA() {
        return mAccounts->a;
    }
    public unsafe BankManager() {
        mAccounts = (Account*)Marshal.AllocHGlobal(sizeof(Account));
    }
    unsafe ~BankManager() {
        if (mAccounts != null) {
             Marshal.FreeHGlobal((IntPtr)mAccounts);
             mAccounts = null;
        }
    }
}

ここでは、構造体をアンマネージ メモリに割り当てました。これにより、変更も移動もしないことがわかっているポインターを保持できます。構造体を使い終わったら、手動で構造体を解放する必要があります。アンマネージ char* (c スタイルの文字列) であるため、mAccounts->mName に対しても同じ手動の割り当て/解放とマーシャリングを行う必要があります。

上記のようなコードは、通常、特定の構造体レイアウトを期待するネイティブ C DllImport エントリポイントと相互運用する場合にのみ使用されるため、このコードの動作を C 対応に近づけるために、構造体にパックされたシーケンシャル レイアウトを作成しました。

于 2013-08-21T18:03:37.207 に答える
0

stringaはポインター参照を持つことができないマネージド型であるため、ポインターを持つことができる型を含む構造体については間違っています。

于 2012-11-08T22:50:51.577 に答える