22

これは私のコードの一部を簡略化したものです。

public struct info
{
    public float a, b;
    public info? c;

    public info(float a, float b, info? c = null)
    {
        this.a = a;
        this.b = b;
        this.c = c;
    }
}

問題は、Struct member 'info' causes a cycle in the struct layout.値型の振る舞いのような構造体の後に発生するエラーです。クラスとクローンメンバー関数を使用してこれをシミュレートすることはできますが、なぜそうする必要があるのか​​わかりません。

このエラーはどのように真実ですか?同様の状況では、再帰によって構築が永久に発生する可能性がありますが、この場合に発生する可能性のある方法は考えられません。以下は、プログラムがコンパイルされる場合に問題ないはずの例です。

new info(1, 2);
new info(1, 2, null);
new info(1, 2, new info(3, 4));

編集:

私が使用した解決策は、「info」を構造体ではなくクラスにし、それを渡すときに使用したコピーを返すメンバー関数を与えることでした。事実上、構造体と同じ動作をシミュレートしますが、クラスを使用します。

また、答えを探しながら次の質問を作成しました。

C#での値型クラス定義?

4

5 に答える 5

35

メンバーとして自分自身を含む構造体を持つことは違法です。これは、構造体のサイズが固定されており、少なくとも各メンバーのサイズの合計と同じ大きさである必要があるためです。infoタイプには、2つのfloatに対して8バイト、 nullかどうかを示すために少なくとも1バイト、および別のサイズが必要infoです。これにより、次の不等式が得られます。

 size of info >= 4 + 4 + 1 + size of info

タイプを無限に大きくする必要があるため、これは明らかに不可能です。

参照型(つまりクラス)を使用する必要があります。クラスを不変にしてオーバーライドし、クラスと同様の値のような動作を与えることがEqualsできGetHashCodeますString

于 2012-02-15T15:36:44.590 に答える
13

これがサイクルを作成する理由は、Nullable<T>それ自体がstructです。参照しているのでinfo、レイアウトにサイクルがあります(infoのフィールドがNullable<info>あり、のフィールドがありますinfo)。これは基本的に次と同等です

public struct MyNullable<T> {
  public T value;
  public bool hasValue;
}

struct info { 
  public float a, b;
  public MyNullable<info> next;
}
于 2012-02-15T15:35:33.213 に答える
5

本当の問題はこの行にあります:

public info? c;

これはであるためstruct、C#は、info外部のレイアウトを生成する前に、内部の/sレイアウトを知る必要がありinfoます。そして、インナーにはインナーインナーinfoが含まれ、インナーインナーinfoにはインナーインナーインナーが含まinfoれます。この循環参照の問題のため、コンパイラはレイアウトを作成できません。

注:は、それ自体が。info? cの省略形です。Nullable<info>struct

于 2012-02-15T15:36:23.057 に答える
2

MyInfo1 = MyInfo2可変サイズのアイテムの可変値セマンティクスを実現する方法はありません(意味的には、MyInfo2によって開始されたものから切り離された新しいリンクリストを生成することが必要だと思います)。info?info[](常にnullであるか、単一要素の配列で埋められている)、またはのインスタンスをラップするホルダークラスに置き換えることができますinfoが、セマンティクスはおそらくあなたが求めているものではありません。に続いてMyInfo1 = MyInfo2、への変更はに影響を与えず、への変更MyInfo1.aは影響を与えMyInfo2.aませんが、への変更はにMyInfo1.c影響を与えます。MyInfo2.cMyInfo1.c[0].aMyInfo2.c[0].a

.netの将来のバージョンに「値の参照」の概念があり、構造体をコピーしてもそのすべてのフィールドが単純にコピーされないようにすると便利です。.netがC++コピーコンストラクターのすべての複雑さをサポートしているわけではないという事実にはある程度の価値がありますが、タイプ'struct'のストレージロケーションに、ストレージロケーションではなくストレージロケーションに関連付けられるIDを持たせることにも価値があります。その内容。

.netは現在そのような概念をサポートしていませんが、変更可能にしたい場合は、info変更可能な参照セマンティクス(保護クローンを含む)または奇妙で奇抜なstruct-class-hybridに我慢する必要があります。セマンティクス。パフォーマンスが懸念される場合の1つの提案は、InfoBase子孫MutableInfoImmutableInfo、および次のメンバーを持つ抽象クラスを持つことです。

  1. AsNewFullyMutable--パブリックインスタンス-ネストされた参照MutableInfoを呼び出して、元のオブジェクトからコピーされたデータを含む新しいオブジェクトを返します。AsNewFullyMutable

  2. AsNewMutable--パブリックインスタンス-ネストされた参照MutableInfoを呼び出して、元のオブジェクトからコピーされたデータを含む新しいオブジェクトを返します。AsImmutable

  3. AsNewImmutable--保護されたインスタンス-元のImmutableInfoオブジェクトからコピーされたデータを含む新しいオブジェクトを返し、ネストされた参照を呼び出しますAsImmutable(ではありません)。AsNewImmutable

  4. AsImmutable-パブリック仮想-の場合、ImmutableInfo自分自身を返します。のために、それ自体MutableInfoを呼び出しAsNewImmutableます。

  5. AsMutable-パブリック仮想-の場合、MutableInfoそれ自体を返します。のために、それ自体ImmutableInfoを呼び出しAsNewMutableます。

オブジェクトのクローンを作成する場合、オブジェクトまたはその子孫が変更される前に再度クローンされると予想されるかどうかに応じて、、、、またはのいずれかAsImmutableを呼び出します。オブジェクトが繰り返し防御的に複製されることが予想されるシナリオでは、オブジェクトは不変のインスタンスに置き換えられ、変更する必要があるまで複製する必要がなくなります。AsNewFullyMutableAsNewMutable

于 2012-02-15T17:28:21.567 に答える
1

免責事項:これは、「値型の振る舞いのような構造体」の目標を達成しない可能性があります。

1つの解決策は、1つのアイテムの配列を使用して、再帰的に参照される構造体の参照を基本的に取得することです。私のアプローチをあなたのコードに適応させることはこのように見えます。

public struct info
{
    public float a, b;
    public info? c
    {
        get
        {
            return cArray[nextIndex];
        }
        set
        {
            steps[nextIndex] = value;
        }
    }
    private info?[] cArray;

    public info(float a, float b, info? c = null)
    {
        this.a = a;
        this.b = b;
        this.cArray = new info?[] { c }
        this.c = c;
    }
}
于 2019-07-15T14:43:43.170 に答える