c#で自動プロパティを使用するには、構造体で:this()を呼び出す必要があるのはなぜですか?

StackOverflow https://stackoverflow.com/questions/272153

質問

次のような自動プロパティを使用してC#で構造体を定義する場合:

public struct Address
{
    public Address(string line1, string line2, string city, string state, string zip)
    {
        Line1 = line1;
        Line2 = line2;
        City = city;
        State = state;
        Zip = zip;
    }

    public string Line1 { get; protected set; }
    public string Line2 { get; protected set; }
    public string City { get; protected set; }
    public string State { get; protected set; }
    public string Zip { get; protected set; }
}

ファイルをビルドしようとすると、The 'this' object cannot be used before all of its fields are assigned toというコンパイルエラーが表示されます。これは、コンストラクターを変更して、次のようにデフォルトコンストラクターへの連鎖呼び出しを行うことで解決できます。

public Address(string line1, string line2, string city, string state, string zip): this()
{
    Line1 = line1;
    Line2 = line2;
    City = city;
    State = state;
    Zip = zip;
}

私の質問は、なぜこれが機能するのか、そして何が起こっているのかということです。私には推測があり、ILを見ることでそれを証明しようとしましたが、ILを破壊できると思う場合にだけ冗談を言っています。しかし、私の推測では、自動プロパティは、コンパイラが背後でプロパティのフィールドを生成することにより機能します。これらのフィールドにはコードを介してアクセスできません。すべての設定と取得はプロパティを介して行う必要があります。構造体を作成するとき、デフォルトのコンストラクターを明示的に定義することはできません。そのため、舞台裏では、コンパイラーは、開発者が見ることができないフィールドの値を設定するデフォルトのコンストラクターを生成する必要があります。

すべてのILウィザードは、私の理論を証明または反証することを歓迎します。

役に立ちましたか?

解決

注:C#6以降、これは必須ではありません-ただし、C#6では読み取り専用の自動実装プロパティを使用する必要があります...

this()は、コンパイラに関する限り、フィールドが確実に割り当てられるようにします-すべてのフィールドをデフォルト値に設定します。 任意のプロパティへのアクセスを開始するには、完全に構築された構造体が必要です。

うっとうしいですが、そうです。本当にこれを構造体にしたいのですか?そして、なぜ構造体で保護されたセッターを使用するのですか?

他のヒント

プロパティは、GetメソッドやSetメソッドのカプセル化にすぎません。 CLRには、特定のメソッドをプロパティと見なす必要があることを示すメタデータがあります。つまり、コンパイラーは、メソッドでは許可されない一部の構造を許可する必要があります。たとえば、XFooの読み取り/書き込みプロパティである場合、コンパイラはFoo.X += 5Foo.SET_X_METHOD(Foo.GET_X_METHOD() + 5)に変換します(ただし、メソッドの名前は異なり、一般的に名前でアクセスすることはできません)。

自動プロパティはget / setメソッドのペアを実装しますが、プロパティの外側のコードの観点からは、フィールドのように動作するようにプライベートフィールドにアクセスしますが、自動プロパティはペアです他のプロパティと同じようにget / setメソッドの数。したがって、Foo.X = 5;のようなステートメントはFoo.SET_X_METHOD(5)に変換されます。 C#コンパイラはそれをメソッド呼び出しとして認識するだけであり、メソッドには読み取りまたは書き込みのフィールドを示すメタデータが含まれていないため、コンパイラはHexDecet<HexDecet<HexDecet<Integer>>>のすべてのフィールドが書き込まれたことを知らない限り、メソッド呼び出しを禁止します。 / p>

個人的に、私のアドバイスは、構造型での自動プロパティの使用を避けることです。クラスのプロパティが更新通知などの機能をサポートできるため、クラスでは自動プロパティが有効です。クラスの初期バージョンが更新通知をサポートしていない場合でも、それらのバージョンがフィールドではなく自動プロパティを使用することは、将来のバージョンがクラスのコンシューマーを作り直すことなく更新通知機能を追加できることを意味します。ただし、構造体は、フィールドのようなプロパティに追加したい機能のほとんどのタイプを意味的にサポートすることはできません。

さらに、フィールドとプロパティのパフォーマンスの差は、クラス構造の場合よりも大きな構造の場合のほうがはるかに大きくなります。実際、大きな構造を避けるための推奨事項の多くは、この違いの結果です。不必要にコピーすることを避ければ、大きな構造は実際に非常に効率的です。 HexDecet<T>がタイプF0の公開フィールドF15 .. Tを含む巨大な構造Foo = MyThing.F3.F6.F9;を持っていたとしても、MyThingのようなステートメントは単にMyThing.F3.F6.F9 += 26;から1つの整数を読み取り、格納する必要があります。 Foo = MyThing.F3.F6.F9は、構造標準(16Kを占める4096整数)によって非常に大きくなりますが、MyThing.F3になります。さらに、その要素を非常に簡単に更新できます。 temp1。対照的に、temp1.F6 .. temp2が自動プロパティである場合、ステートメントtemp2.F9では、1KのデータをMyThing.F3.F6.F9からテンポラリにコピーする必要があります(var t1 = MyThing.F3; var t2 = t1.F6; t2.F9 += 26; t1.F6 = f2; MyThing.F3 = t1;と呼び、次に64バイトのデータを<= >からArraySegment<T>)に移動してから、最終的にVar foo[] = new int[100]; Var MyArrSeg = New ArraySegment<int>(foo, 25, 25); MyArrSeg[6] += 9;から4バイトのデータを読み取ります。うんさらに悪いことに、fooの値に26を追加しようとすると、<=>のようなものが必要になります。

<!> quot;可変構造型<!> quotに関する長年の苦情の多く;読み取り/書き込みプロパティを持つ構造タイプに関する実際の苦情です。プロパティをフィールドに置き換えるだけで、問題はなくなります。

PS:プロパティが参照を保持するクラスオブジェクトにアクセスするプロパティを持つ構造があると便利な場合があります。たとえば、<=>と言うことができる<=>クラスのバージョンがあり、最後のステートメントで<=>の要素(25 + 6)に9を追加するとよいでしょう。古いバージョンのC#では、これを行うことができました。残念ながら、フィールドがより適切であったフレームワークで自動プロパティを頻繁に使用すると、読み取り専用構造でプロパティセッターを無用に呼び出すことができるコンパイラに関する苦情が広まりました。その結果、プロパティセッターが構造体のフィールドを実際に変更するかどうかに関係なく、読み取り専用構造でプロパティセッターを呼び出すことは禁止されています。プロパティセッターを介して構造体を変更可能にすること(可変性が適切な場合にフィールドに直接アクセスできるようにすること)を単に控えた場合、コンパイラはその制限を実装する必要がなかったでしょう。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top